PATTERN Cited by 1 source
Manifest-driven micro-frontend loading¶
Problem¶
A micro-frontend host needs to decide which remote bundles to load for the current user. Hard-coding that mapping inside the host's Webpack config has two drawbacks:
- Adding or removing a remote requires a host rebuild. This defeats much of the independent-deploy promise of Module Federation.
- Every user gets every remote loaded. There is no way to scope remotes to the user's permissions, A/B groups, or market — the shell either renders them or hides them with client-side checks.
A second problem is per-app metadata — menu labels, required permissions, bundle paths, and per-app configuration need to come from somewhere the host can consume without shipping every app's data in its own bundle.
Solution¶
Replace the static remotes map with a two-step, server- authoritative discovery flow:
- Applications endpoint. The host calls a server-side
/applicationsendpoint that returns only the apps the authenticated user is authorised to see. Each entry carries: - an ID and display name,
- an active path (the route where the app takes over),
- a permission scope object (typically from an OPA- style policy engine) describing read/write permissions per scope,
-
a path to a per-app manifest (typically a
manifest.json). -
Per-app manifest. For each app in the list, the host fetches the
manifest.json. The manifest declares: - menu items (label, path, required permissions, group),
- bundle path (the URL to
RemoteEntry.js), - app-scoped configuration.
The host renders the menu, reads requiredPermissions
to gate entries, and lazy-loads the remote's bundle
only when the user navigates to its activePath.
Trade-offs¶
Pro
- Permission-scoped UI. The shell only knows about apps
the user can access; menu + bundle loading are both
naturally gated.
- No host redeploy to add an app. New apps are added
by publishing a manifest.json + bundle and updating the
server's /applications response.
- Per-app config lives with the app. The manifest is
owned by the app team, not embedded in the host.
- Lazy loading by construction. Remotes are fetched on
navigation, not at shell load.
Con - Two extra round trips on shell start-up (applications endpoint + N manifests). Cacheable but non-zero. - The manifest contract is load-bearing across all apps — breaking it breaks every remote. - Requires a server component (the applications endpoint) — usually the same backend proxy that handles auth (see patterns/centralised-backend-proxy-for-micro-frontends). - Remotes still need to declare their Module Federation name and shared libraries at build time — the manifest is discovery + metadata, not federation config.
Seen in¶
- sources/2024-10-16-zalando-building-a-modular-portal-with-webpack-module-federation
— canonical wiki instance. Zalando's Logistics Portal
calls the portal proxy's
/applicationsendpoint on load, receives a permission-scoped list of apps (each withappId,name,configPath,activePath,opaScope), fetches eachmanifest.json(withmenuItemscontainingrequiredPermissionsandbundlePath), and lazy-loads the remote's bundle onactivePathnavigation. Verbatim from the post: "Each application comes with its ID, name, configuration path, and the specific URL (activePath) where the application will start loading into the portal. Additionally, it contains detailed permission scopes (e.g., read and write permissions) for specific actions within the application." The manifest carriesmenuItems+bundlePathseparately from the applications response so per-app menu structure stays with the app team.
Related¶
- concepts/micro-frontends — the architecture class this pattern serves.
- concepts/module-federation-host-remote — the topology the manifest layer sits above.
- systems/webpack-module-federation — the underlying federation mechanism.
- patterns/centralised-backend-proxy-for-micro-frontends
— usually owns the
/applicationsendpoint. - patterns/host-shell-prop-api-for-remotes — communication seam above the loaded remote.
- systems/zalando-logistics-portal — canonical wiki deployment.