Skip to content

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:

  1. Adding or removing a remote requires a host rebuild. This defeats much of the independent-deploy promise of Module Federation.
  2. 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:

  1. Applications endpoint. The host calls a server-side /applications endpoint that returns only the apps the authenticated user is authorised to see. Each entry carries:
  2. an ID and display name,
  3. an active path (the route where the app takes over),
  4. a permission scope object (typically from an OPA- style policy engine) describing read/write permissions per scope,
  5. a path to a per-app manifest (typically a manifest.json).

  6. Per-app manifest. For each app in the list, the host fetches the manifest.json. The manifest declares:

  7. menu items (label, path, required permissions, group),
  8. bundle path (the URL to RemoteEntry.js),
  9. 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 /applications endpoint on load, receives a permission-scoped list of apps (each with appId, name, configPath, activePath, opaScope), fetches each manifest.json (with menuItems containing requiredPermissions and bundlePath), and lazy-loads the remote's bundle on activePath navigation. 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 carries menuItems + bundlePath separately from the applications response so per-app menu structure stays with the app team.
Last updated · 550 distilled / 1,221 read