-
Notifications
You must be signed in to change notification settings - Fork 4
State Management
@microfrontend provides a mechanism to prevent accidental data loss when users navigate away from a microfrontend that has unsaved changes ("dirty state").
1. Microfrontend reports dirty state → routedApp.changeState(true)
2. User tries to navigate away → router.go('other-app')
3. Shell checks for dirty state → Calls registered callback
4. Callback returns true/false → Navigation proceeds or is blocked
5. If navigation proceeds → Shell sends discard notification
6. Microfrontend resets its state → routedApp.changeState(false)
Use RoutedApp.changeState() to tell the shell whether the microfrontend has unsaved data:
// User starts editing a form — mark as dirty
routedApp.changeState(true, 'edit-form');
// User saves or cancels — mark as clean
routedApp.changeState(false, 'edit-form');| Parameter | Type | Description |
|---|---|---|
hasState |
boolean |
true if there is unsaved state, false if clean |
subRoute |
string | undefined |
The subroute associated with the state |
Register a callback on the MetaRouter that decides whether to allow navigation away from a dirty microfrontend:
router.registerAllowStateDiscardCallbackAsync(
async (metaRoute: string, subRoute?: string) => {
// Show a confirmation dialog
return confirm(`You have unsaved changes in ${metaRoute}. Discard?`);
},
MetaRouteStateEvaluation.RouteBased
);- If the callback returns
true→ navigation proceeds - If the callback returns
false→ navigation is rejected and thego()promise rejects
The MetaRouteStateEvaluation enum determines how the shell evaluates whether a microfrontend is dirty:
The microfrontend is considered dirty only if the currently active subroute has reported state. If the user is on a different subroute within the same microfrontend, the state is not triggered.
router.registerAllowStateDiscardCallbackAsync(callback, MetaRouteStateEvaluation.RouteBased);Use this when dirty state is tied to a specific view/page within the microfrontend.
The microfrontend is considered dirty if any subroute has reported state. The specific subroute does not matter.
router.registerAllowStateDiscardCallbackAsync(callback, MetaRouteStateEvaluation.AppBased);Use this when dirty state applies to the entire microfrontend, regardless of which subroute the user is on.
When the shell confirms navigation (the callback returned true), it sends a MessageStateDiscard to the microfrontend. Register a callback in the microfrontend to handle this:
routedApp.registerDiscardStateCallback(() => {
// Reset form, clear local state, etc.
form.reset();
routedApp.changeState(false);
});const router = new MetaRouter(config);
// Register state discard confirmation
router.registerAllowStateDiscardCallbackAsync(
async (metaRoute, subRoute) => {
// Custom confirmation dialog
return new Promise((resolve) => {
showDialog({
title: 'Unsaved Changes',
message: `Discard changes in ${metaRoute}${subRoute ? '/' + subRoute : ''}?`,
onConfirm: () => resolve(true),
onCancel: () => resolve(false)
});
});
},
MetaRouteStateEvaluation.RouteBased
);// Track form changes
form.addEventListener('input', () => {
routedApp.changeState(true, 'edit');
});
// Handle save
saveButton.addEventListener('click', () => {
saveData();
routedApp.changeState(false, 'edit');
});
// Handle forced discard from the shell
routedApp.registerDiscardStateCallback(() => {
form.reset();
routedApp.changeState(false);
});Microfrontend Shell
│ │
│──changeState(true, 'edit')─►│ User edits form
│ │
│ │ User clicks navigate
│ │──checkIfRoutingAllowed()
│ │ (has state? → yes)
│ │──callback('app-a', 'edit')
│ │ (user confirms → true)
│ │
│◄─MessageStateDiscard────────│ Shell tells MF to discard
│ │
│──changeState(false)────────►│ MF resets and reports clean
│ │
│ │──activateRoute('app-b')
│ │ Navigation proceeds
@microfrontend — MIT License — npm — GitHub
Getting Started
Guides
API Reference
Contributing