single-spa Integration
Single-Spa Integration for Micro Frontends¶
Single-Spa is a framework for composing multiple independent applications (sub-apps) into a single-page application (SPA) by unifying routing, lifecycle management, and communication. It enables micro frontends to share a global state, coordinate navigation, and manage dependencies without requiring full page reloads. This section demonstrates how to integrate micro frontends with Single-Spa for unified routing and lifecycle orchestration.
High-Level Architecture Overview¶
Single-Spa operates by registering sub-apps with a central registry and using a routing strategy to determine which sub-app should be active for a given URL. Key components include:
- Root App: The main application that bootstraps Single-Spa and manages global routing.
- Sub-Apps: Independent applications (e.g., React, Vue, Angular) registered with Single-Spa.
- Lifecycle Hooks: bootstrap, mount, and unmount methods to control sub-app behavior.
A diagram of this architecture would show the root app at the top, sub-apps as separate modules, and Single-Spa managing their activation based on URL routes.
Step-by-Step Integration¶
1. Initialize Single-Spa in the Root App¶
Install the core library:
Configure the root app to register sub-apps and define routing:
// root-app.js
import { setConfig, singleSpaAngular, singleSpaReact } from 'single-spa';
// Register Angular sub-app
singleSpaAngular({
bootstrapFunction: angular.bootstrap,
template: '<app-root></app-root>',
prodMode: true,
ngModule: AppModule,
rootElement: '#angular-root'
});
// Register React sub-app
singleSpaReact({
lifecycle: {
bootstrap({ props }) {
return Promise.resolve();
},
mount({ props }) {
ReactDOM.render(<App />, document.getElementById('react-root'));
return Promise.resolve();
},
unmount() {
ReactDOM.unmountComponentAtNode(document.getElementById('react-root'));
}
},
// Use React Router for dynamic routing
customProps: ({ location }) => ({ location })
});
2. Define Sub-App Lifecycle Hooks¶
Sub-apps must export lifecycle methods to integrate with Single-Spa. For example, a React sub-app:
// sub-app-react/index.js
export const bootstrap = ({ props }) => {
console.log('React sub-app bootstrapped', props);
return Promise.resolve();
};
export const mount = ({ props }) => {
console.log('React sub-app mounted', props);
return Promise.resolve();
};
export const unmount = () => {
console.log('React sub-app unmounted');
};
3. Configure Routing¶
Single-Spa supports multiple routing strategies:
- Hash History: Uses # in URLs (e.g., /#/dashboard).
- HTML5 History: Uses pushState (e.g., /dashboard).
- Dynamic Routing: Register sub-apps conditionally based on routes.
Example with HTML5 history:
import { setConfig } from 'single-spa';
setConfig({
historyApiFallback: true,
routes: {
'/dashboard': 'react-dashboard',
'/settings': 'angular-settings'
}
});
Advanced Patterns¶
1. Dynamic Sub-App Loading¶
Load sub-apps on demand using singleSpa.registerApplication:
singleSpa.registerApplication(
'dynamic-subapp',
() => System.import('subapp-bundle'),
(location) => location.pathname.startsWith('/dynamic')
);
2. Cross-App Communication¶
Use Single-Spa's global API to communicate between sub-apps:
// In sub-app A
singleSpa.getSingleSpa().notify('subapp-b', { message: 'Hello from A' });
// In sub-app B
singleSpa.getSingleSpa().on('subapp-b', (data) => {
console.log('Received:', data);
});
3. Error Handling¶
Implement fallbacks for sub-app failures:
singleSpa.registerApplication(
'error-subapp',
() => import('subapp-bundle').catch((err) => {
console.error('Sub-app failed to load:', err);
return Promise.reject(err);
}),
(location) => location.pathname === '/error'
);
Key takeaways¶
- Unified routing via Single-Spa ensures consistent navigation across sub-apps.
- Lifecycle hooks (
bootstrap,mount,unmount) enable granular control over sub-app behavior. - Dynamic loading and cross-app communication enhance flexibility and modularity.
- Error handling and routing strategies (hash/HTML5) are critical for robust integration.