Skip to content

Route-Level Async Initializer for Runtime Configuration (Microfrontends / Module Federation) #67874

@vabdushelishvili-tbc

Description

@vabdushelishvili-tbc

Which @angular/* package(s) are relevant/related to the feature request?

Problem

In microfrontend architectures using Module Federation, Angular
applications often expose either routes or standalone components from
remote builds.

A common real-world requirement is to fetch runtime configuration from
a backend before initializing routes or components
(e.g., feature
flags, dynamic route definitions, environment-specific endpoints).

Currently, Angular provides:

  • APP_INITIALIZER --- only runs at application bootstrap (too early
    / global)
  • Route guards and resolvers --- run after route matching
  • Route providers --- scoped DI, but no async blocking mechanism

This creates a gap when:

  • Routes depend on async configuration
  • Routes must be dynamically constructed at runtime
  • Application must support deep linking (page refresh on nested
    routes)

Current Limitation

If routes are built dynamically from fetched configuration:

  • On page refresh (deep link), Angular router attempts to match routes
    before configuration is loaded
  • Since routes are not yet registered → navigation falls through to
    wildcard (404)

Guards/resolvers do not solve this because:

  • They execute after route matching
  • They cannot prevent router from failing to recognize a route

APP_INITIALIZER is also not suitable because:

  • It is global (not route-scoped)
  • It does not integrate cleanly with lazy-loaded MFEs
  • It forces all configuration to load upfront, even when not needed

Proposed Solution

Introduce a route-level async initializer that runs before route
matching and activation
, allowing async setup of route configuration.

Proposed solution

Example API

{
  path: '',
  providers: [
    provideRouteInitializer(async () => {
      const configService = inject(ConfigService);
      const routes = await configService.getRoutes();

      const router = inject(Router);
      router.resetConfig(routes);
    })
  ],
  loadChildren: () => import('./remote.routes')
}

Alternatives considered

no idea tbh.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: routergemini-triagedLabel noting that an issue has been triaged by gemini

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions