Skip to content

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:

npm install single-spa

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.