WU / DOCSUNIVERSAL MICROFRONTENDS
EN

Navigation and composition

Connect the shell's URL and navigation intents to application mounting, unmounting, and state.

The shell owns navigation

The shell decides which application corresponds to each route and uses mounting, unmounting, events, and state to compose it. Astro can resolve pages; a React/Vue router can manage an application’s internal routes. WU core does not include wu.router or wu.navigate(): connect your shell’s navigation mechanism to its lifecycle.

metadata.routes in wu.json is descriptive information. The parser validates strings, duplicates, and limits but does not install History API handlers. Nonempty wu.routes is rejected in Framework 4 and must not be used as a routing recipe.

Two routing levels

Level Example Owner
Portal page /docs/framework/state Astro file router
Dynamic shell section /workspace/catalog Shell controller/router
Internal app view /products/:id within catalog That app’s router

The download base URL, such as /apps/catalog, is a technical resource and need not match the navigation route. Define the bundle base, internal router base, and user-visible route separately.

Connect a navigation intent

This example implements a shell convention; shell:navigate does not activate a built-in router:

const routeTable = new Map([
  ['/workspace/catalog', 'catalog'],
  ['/workspace/reports', 'reports'],
]);

let activeApp = null;
let transition = Promise.resolve();

function showRoute(path) {
  transition = transition.catch(() => {}).then(async () => {
    const nextApp = routeTable.get(path);
    if (!nextApp || nextApp === activeApp) return;
    if (activeApp) await wu.unmount(activeApp, { force: true });
    activeApp = null;
    await wu.mount(nextApp, '#workspace-slot');
    activeApp = nextApp;
  });
  return transition;
}

const stop = wu.on('shell:navigate', ({ data }) => {
  if (!routeTable.has(data?.path)) return;
  history.pushState(null, '', data.path);
  showRoute(data.path).catch(console.error);
});

const onPopState = () => showRoute(location.pathname).catch(console.error);
window.addEventListener('popstate', onPopState);
await showRoute(location.pathname);

// Al retirar el controlador del shell:
// stop();
// window.removeEventListener('popstate', onPopState);

The table limits accepted routes and apps. The queue preserves order when the user navigates several times before loading finishes. The core also coordinates container ownership; maintaining clear shell state prevents an old promise from updating navigation controls for a newer route.

Elements inside Shadow DOM

wu.mount(name, selector) resolves the selector with document.querySelector. That selector does not cross ShadowRoots, and the current signature expects a string, not an HTMLElement. To let the shell locate the slot, place it in its light DOM or use a composition that exposes a selector-accessible node.

Inside a lifecycle, use the supplied container to manipulate the local root; do not search for it globally by ID again. The repository’s Vue adapter illustrates why a stable root should be a sibling of the loading indicator, not its shell-managed container.

Keep-alive and route changes

hide preserves state and listeners; it works when you retain the app’s container. Switching many apps on one slot requires an explicit decision: unmount the previous owner or keep separate containers for each app that remains alive. Do not equate keep-alive with caching modules; they are different resources.

If Astro replaces pages through client navigation, bootstrapping must follow that lifecycle: release listeners and mounts from the previous page and connect the new slots. A deduplicated script import does not mean all future nodes are mounted automatically.

UI roles

wu.setPrincipal({ id: 'u1', roles: ['catalog-reader'] }) feeds wu.can('catalog'). Mounting checks the roles required by the manifest and may reject with WU_ACCESS_DENIED. This helps keep navigation and views consistent; data APIs must authorize every request on the server.

Implementation references

Descriptive route validation: src/core/wu-manifest.js; access: src/core/wu-core.js and src/core/wu-core.js; selector: src/core/wu-core.js; container transition: src/core/wu-core.js; convention used by MCP: src/core/wu-mcp-bridge.js. Tests: tests/unit/shared-container-navigation.test.js, tests/unit/sandbox-container-ownership.test.js, tests/unit/rbac.test.js.

BUILD WU WITH US

Found a bug? Have an idea?

Your experience improves WU. Tell us what went wrong or help support its development.