Zalando — Building a Modular Portal with Webpack Module Federation¶
Summary¶
Zalando's Transport teams inside the Logistics department
describe the architecture of an internal portal they built
to serve finance teams, warehouses, and (planned) third-party
partners, using Webpack
Module Federation as the runtime composition primitive. This
is explicitly not the public zalando.com Fashion Store
stack (which runs on
Interface Framework —
see sources/2021-03-10-zalando-micro-frontends-from-fragments-to-renderers-part-1);
it is a separate, inward-facing portal in a different
part of the company with different constraints (mixed
greenfield + legacy iframe-hosted apps, five collaborating
teams, small team count per app).
At the time of writing the portal hosts 11 applications developed by 4 teams. The architectural thesis is simple: each team should develop and deploy an application independently without rebuilding the host portal or coordinating a site-wide release. Module Federation delivers this by letting the host load a remote bundle (the team's application) at runtime from a URL the team controls, with shared libraries (React, React-DOM) resolved to a single version across host + remotes.
The interesting content is how the seams are drawn around Module Federation:
- A centralised backend proxy inside the portal acts as the single authentication + authorisation gatekeeper for all backend calls from every micro-frontend. No remote talks directly to a microservice; everything flows through the portal proxy.
- Manifest-driven application loading. On portal load,
the frontend calls the proxy's
/applicationsendpoint, which returns only applications the authenticated user can access, each with an OPA-style permission scope object, anactivePath, and a path to amanifest.json. The frontend fetches each manifest, readsbundlePath(pointing toRemoteEntry.js), and lazily loads the remote when the user navigates to its route. - A prop-based shell API. The host passes a single prop to every remote application, serving as the interface for session info, global settings, cross-remote actions (portal-wide logout, navigation, error reporting), and data sharing. Remotes stay independent but gain a typed channel to the portal.
- A shared UI-kit distributed as an internal npm package. Design consistency across the 11 applications is kept by a reusable component library — one place to update the design language, propagated by bumping the npm dependency.
The post positions Module Federation explicitly against two prior approaches the portal needed to escape: monolithic static builds (release coupling across teams, coordination delays) and iframe-embedded applications (non-scalable, lacking modern integration primitives like shared React instance, cross-frame navigation, unified auth).
Key takeaways¶
- Runtime code sharing is the distinguishing primitive. Webpack Module Federation's value vs. a shared-npm-package approach is that remotes are shared at runtime without host rebuilds: each team can expose components / utilities from its bundle, and other bundles consume them without re-publishing or re-deploying a shared library (Source: section 2; concepts/runtime-code-sharing).
- Shared dependencies must be declared as singletons to
avoid runtime breakage. With multiple remotes, the
primary class of bug is two remotes each bundling their
own React, producing runtime errors or "unexpected
behaviour". Webpack's
sharedconfiguration marks common libraries (e.g. React, React-DOM, lodash) as singletons so only one version loads at runtime; teams additionally align versions during development (Source: section 5; concepts/shared-singleton-dependency). - A centralised backend proxy is the auth-gatekeeper seam. Every request from any micro-frontend goes through the portal proxy, which authenticates the user once and forwards authorised requests to the appropriate microservice. Micro-frontends don't implement auth flows; microservices don't implement authorisation logic. The proxy is the one place permissions are checked (Source: section 3; patterns/centralised-backend-proxy-for-micro-frontends).
- Manifest-driven loading gives personalised + permission-
scoped navigation. The
/applicationsendpoint returns only the apps the user can access, each with anopaScopeobject describing read/write permissions per scope. The portal fetches each app'smanifest.json(declaringmenuItemswithrequiredPermissionsandbundlePath) and lazy-loads the app'sRemoteEntry.jsonactivePathnavigation — the UI shape follows the user's permissions (Source: section 4; patterns/manifest-driven-micro-frontend-loading). - Communication between remotes is mediated by a single prop passed from the host. Rather than a global event bus or cross-bundle imports, each federated module receives a prop from the portal. The prop exposes session info, global settings (theme, locale), portal- wide actions (logout, navigate, errorReport), and data handoffs. This keeps each remote independent while giving it a typed, governed interface to the shell (Source: section 5 "Communication Between Apps"; patterns/host-shell-prop-api-for-remotes).
- Lazy loading + code splitting keep the shell fast despite many remotes. Federated modules are loaded only when the user navigates to their section. Webpack configuration enables code splitting, caching, and compression; preloading critical assets for likely-next interactions narrows perceived load time. Without this, the cost model gets worse with every added application (Source: section 5 "Performance Considerations").
- A shared UI-kit as internal npm package keeps design consistent across the 11 apps. Buttons, modals, input fields, typography all live in one reusable library; central design updates propagate to all remotes on dependency bump. Without this, each team re-creates UI primitives and the portal drifts visually (Source: section 7; patterns/shared-ui-kit-as-internal-npm).
- Interfaces must be designed early, not discovered.
Lessons learned: even with Module Federation's runtime
flexibility, "defining clear interfaces and communication
methods helped avoid complexity later on". The prop API
and
manifest.jsoncontract are load-bearing: retrofitting either after 11 apps exist is expensive (Source: section 6; the authors flag this as the top lesson). - Module Federation is an integration path away from iframes. The legacy context matters: some of the portal's applications were still being used as iframes inside other portals. Module Federation let them become first-class remotes in the new portal while still running in iframe form elsewhere, avoiding a forced rewrite (Source: section 1).
Operational numbers¶
From the post:
- 5 teams collaborated on the portal project.
- 4 teams currently develop the portal's applications.
- 11 applications currently hosted by the portal.
- Host + remotes share React and React-DOM as singletons
via Webpack's
sharedconfig. - Telemetry, QPS, latency, bundle-size, and per-app traffic numbers are not disclosed in the post — consistent with its framing as an internal-tooling case study rather than a production-scale performance writeup.
Systems extracted¶
- systems/webpack-module-federation — Webpack feature for runtime code sharing between bundles; the portal's core composition primitive. Canonical wiki instance anchored by this source.
- systems/zalando-logistics-portal — the Zalando Transport teams' internal portal; first wiki instance of a Module-Federation-based Zalando frontend, distinct from Interface Framework.
- systems/webpack — underlying bundler (exists implicitly via many wiki pages; this source adds a Module Federation Seen-in dimension).
- systems/react — shared singleton dependency across
host and remotes; already a rich wiki system; this source
adds the
shared: { react: { singleton: true } }deployment.
Concepts extracted¶
- concepts/micro-frontends — updated with the Module-Federation variant alongside the Mosaic-Fragment and Interface-Framework-Renderer variants already on the page.
- concepts/module-federation-host-remote — the host / remote / expose / consume runtime topology introduced by Webpack Module Federation.
- concepts/runtime-code-sharing — distinguishing primitive vs. static linking or shared-npm-package publishing; the load-bearing property Module Federation trades on.
- concepts/shared-singleton-dependency — the
singleton: truesemantic that makes shared libraries (React, React-DOM) safe to load once across federated modules.
Patterns extracted¶
- patterns/centralised-backend-proxy-for-micro-frontends — single-gatekeeper proxy fronting all backend calls from all remotes; authenticates once, forwards authorised requests. The auth / authz seam for a Module-Federation portal.
- patterns/manifest-driven-micro-frontend-loading —
/applicationsendpoint → per-appmanifest.json→RemoteEntry.jslazy load chain, permission-scoped per user. - patterns/host-shell-prop-api-for-remotes — single prop passed from host to every remote, serving as the governed communication channel (session info, settings, actions, errors).
- patterns/shared-ui-kit-as-internal-npm — reusable component library published as an internal npm module, propagated across remotes on dependency bump; centralises the design language without coupling deployments.
Context on the wiki¶
This is the second micro-frontend architecture on the
Zalando axis. It does not replace or succeed the
Interface
Framework described in
sources/2021-03-10-zalando-micro-frontends-from-fragments-to-renderers-part-1
(which serves zalando.com customer traffic); the two are
separate platforms in separate parts of the company with
different constraints:
| Platform | Audience | Composition primitive | Article |
|---|---|---|---|
| Mosaic (2015) | Fashion Store customers | Fragments (frontend+backend pair per slot) | 2021-03-10 |
| Interface Framework (2018) | Fashion Store customers | Entity → Renderer (one React component per Entity type) | 2021-03-10, 2023-07-10 |
| Logistics Portal (2024) | Internal Logistics / Transport users | Webpack Module Federation remotes | This post |
Interface Framework is a unified Rendering Engine that picks Renderers at request time for consumer traffic; the Logistics Portal is a host shell that loads independently- built Webpack remotes at runtime for internal users. They share the phrase "micro-frontend" and not much else structurally.
Caveats¶
- Not a performance writeup. The post is an architecture case study; it does not disclose QPS, latency, bundle sizes, fleet size, or production incident data. There is no measured comparison against the pre-Module-Federation (monolith / iframe) approach beyond qualitative claims.
- Not customer-facing scale. 11 applications / 4 teams / internal users is a substantially smaller scale than the Fashion Store Interface Framework's "hundreds of Renderers". The patterns here fit internal-portal altitude; the tradeoffs may not transfer to a customer-traffic setting.
- OPA-style permission scopes are only sketched. The
opaScopeobject in the/applicationsresponse hints at OPA (Open Policy Agent) integration, but the post does not describe the OPA deployment, policy authorship flow, or the backend proxy's evaluation model. - No discussion of version-skew dynamics between host + remote. Shared dependencies are aligned during development but the post doesn't cover what happens when the host upgrades React while a team's remote hasn't yet rebuilt — a real operational concern in Module Federation deployments.
- No discussion of runtime auth between remotes and the prop API. The prop mediates communication, but whether the prop itself is tamper-resistant (e.g. a frozen object or an iframe-sandboxed context) isn't described.
Source¶
- Original: https://engineering.zalando.com/posts/2024/10/building-modular-portal-with-webpack-module-federation.html
- Raw markdown:
raw/zalando/2024-10-16-building-a-modular-portal-with-webpack-module-federation-0714c84b.md
Related¶
- systems/webpack-module-federation · systems/zalando-logistics-portal · systems/webpack · systems/react
- concepts/micro-frontends · concepts/module-federation-host-remote · concepts/runtime-code-sharing · concepts/shared-singleton-dependency
- patterns/centralised-backend-proxy-for-micro-frontends · patterns/manifest-driven-micro-frontend-loading · patterns/host-shell-prop-api-for-remotes · patterns/shared-ui-kit-as-internal-npm
- Contrasting Zalando micro-frontend architectures: sources/2021-03-10-zalando-micro-frontends-from-fragments-to-renderers-part-1 · systems/zalando-interface-framework · systems/zalando-mosaic · systems/zalando-rendering-engine
- companies/zalando