single-spa - Documentation

Getting Started

What is single-spa?

single-spa is a JavaScript framework for front-end microservices. It allows you to build independent, deployable applications (often called “microservices” or “microfrontends”) that can be combined to create a larger, more complex application. Instead of a monolithic application, you have multiple smaller applications working together. This approach offers benefits such as improved code organization, independent deployments, and better team scalability. single-spa handles the routing and lifecycle management of these individual applications, ensuring seamless integration and navigation between them.

Installation

To get started with single-spa, you’ll need to install it using npm or yarn. Open your terminal and run the following command:

npm install single-spa
# or
yarn add single-spa

This will install the core single-spa library in your main application. You will also need to install separate packages for each of your microfrontends, though their installation depends on the framework they’re built with (React, Angular, Vue, etc.).

Creating your first single-spa application

This section guides you through creating a simple single-spa application with a single microfrontend. We will use Parcel for simplicity, but you can adapt to Webpack or other bundlers.

  1. Create a root application: Create a new directory for your root single-spa application (e.g., my-single-spa-app). Inside this directory, create the following files:

  2. index.html:

    <!DOCTYPE html>
    <html>
      <head>
        <title>My Single-SPA App</title>
      </head>
      <body>
        <div id="root"></div>
        <script src="/index.js"></script>
      </body>
    </html>
  3. index.js:

    import { registerApplication, start } from 'single-spa';
    
    registerApplication(
      'app1',
      () => import('./app1.js'), //Import function for your app
      () => location.pathname.startsWith('/app1') // active when route starts with '/app1'
    );
    
    start();
  4. app1.js (simple React example): Create app1.js in the same directory. This example assumes you have a basic React project set up already.

    export async function bootstrap(props) {
      console.log('app1 bootstrapped');
      // your application's bootstrap logic, which will be called once before mounting.
    }
    
    export async function mount(props) {
       const root = document.createElement('div');
       root.id = 'root-app1';
       document.body.appendChild(root);
    
       //Mount your React app here, eg. ReactDOM.render(<App />, root);
       console.log('app1 mounted');
    }
    
    export async function unmount(props) {
      // your application's unmount logic, called once before the application is unmounted.
       console.log('app1 unmounted');
    }

Remember to replace the placeholder comment in app1.js with the actual mounting logic for your React app.

Running your application

You can use a simple static file server (like parcel) or a more sophisticated setup (like Webpack Dev Server). For this example, parcel is used:

  1. Install Parcel: npm install -D parcel
  2. Run Parcel: parcel index.html

Navigate to http://localhost:<port> (the port will be displayed in your terminal) in your browser. You should see your app1. Remember to navigate to /app1 in your URL to activate your application.

Understanding the core concepts

single-spa relies on several key concepts:

Understanding these core concepts is fundamental to building robust and scalable single-spa applications. Each concept will be elaborated on in later sections of this manual.

Defining Applications

Registering applications

Registering an application with single-spa makes it known to the single-spa router. This is done using the registerApplication function. This function takes three arguments:

  1. name (String): A unique name for your application. This name is used internally by single-spa to identify and manage the application.

  2. app (Function): A function that returns a promise which resolves to the application’s exported lifecycle functions (bootstrap, mount, unmount). This function is typically implemented using dynamic imports (import()). This allows for lazy loading of your applications.

  3. activeWhen (Function): A function that returns a boolean indicating whether the application should be active based on the current URL or any other criteria. This function is called frequently by single-spa to determine which application(s) should be mounted.

Example:

import { registerApplication } from 'single-spa';

registerApplication(
  'app1',
  () => import('./app1.js'),
  location => location.pathname === '/app1'
);

In this example:

Application lifecycles (mounting, booting, unmounting)

Each single-spa application exposes three lifecycle functions:

These functions are passed a props object containing useful information such as the application’s name and customProps (more on this below). Each function should return a promise that resolves once the operation is complete.

Application URLs and routing

single-spa doesn’t handle routing within your microfrontends. It only manages the activation and deactivation of microfrontends based on the URL. Your activeWhen function dictates which application is active based on the current URL. You can use any logic you want within activeWhen, including regular expressions for more complex routing.

Example using a regular expression:

registerApplication(
  'app2',
  () => import('./app2.js'),
  location => location.pathname.startsWith('/dashboard')
);

This registers app2 to be active whenever the URL starts with /dashboard. The internal routing within app2 would then handle navigation within the dashboard section.

Configuring application loading strategies

By default, single-spa loads applications using dynamic imports. However, you can customize the loading strategy. For instance, you might want to pre-load certain applications to improve perceived performance or have a different mechanism for loading certain applications. This is often achieved by modifying the app function passed to registerApplication.

Working with different frameworks (React, Angular, Vue, etc.)

single-spa is framework-agnostic. You can build your microfrontends using any framework (React, Angular, Vue, Svelte, etc.). The key is that each microfrontend must export the bootstrap, mount, and unmount lifecycle functions in a way that single-spa understands. The implementation of these functions will naturally vary depending on the framework you’re using, but the interface to single-spa remains the same.

Advanced application configurations

customProps: You can pass custom data to your applications via the customProps option in the registerApplication function:

registerApplication(
  'app3',
  () => import('./app3.js'),
  location => location.pathname === '/app3',
  {
      customProp1: 'value1',
      customProp2: 123
  }
);

This passes {customProp1: 'value1', customProp2: 123} to app3’s lifecycle functions in the props object.

loadError Handling: You can handle errors during the loading of your applications using a .catch() on the promise returned by the app function provided to registerApplication.

Creating and using custom lifecycles

While bootstrap, mount, and unmount are the standard lifecycle functions, you can extend single-spa’s functionality by creating and using custom lifecycle functions. These are added using the addErrorHandler, addLifeCycle functions. This allows you to add extra steps to the application lifecycle without modifying the core single-spa behavior. Refer to the official single-spa documentation for details on how to implement custom lifecycles.

Parceling and Optimization

Understanding single-spa’s parceling approach

single-spa itself doesn’t handle bundling or code splitting. It relies on your build process (e.g., Webpack, Parcel, Rollup) to create separate bundles for each of your microfrontends. The key is that each microfrontend should be independently buildable and deployable. single-spa’s role is to orchestrate the loading and lifecycle management of these pre-built bundles. It dynamically loads these bundles using techniques like dynamic import() statements, allowing for efficient lazy-loading.

Optimizing application load times

Optimizing load times is crucial for a positive user experience in a single-spa application. Here are some strategies:

Code-splitting and lazy-loading

Code splitting is essential for optimizing single-spa applications. Instead of having one large bundle for each microfrontend, split your code into smaller chunks based on functionality or features. This reduces the initial load time and allows the browser to only download the code needed for the currently active application. Modern bundlers like Webpack and Parcel offer robust support for code splitting. The import() statement is how single-spa facilitates the lazy loading of these code chunks on demand.

Caching strategies

Effective caching can significantly improve performance. Leverage browser caching to avoid repeatedly downloading the same application code. This includes:

Working with different build tools

single-spa is compatible with various build tools like Webpack, Parcel, Rollup, and others. The choice of build tool depends on your project’s needs and preferences. Each tool has its own mechanisms for code splitting, optimization, and caching. It’s crucial to configure your chosen build tool correctly to optimize your single-spa application. Ensure your configuration leverages features like code splitting, minification, and appropriate caching strategies.

Performance considerations

Performance considerations are crucial for a successful single-spa architecture:

By focusing on these optimization strategies, you can build high-performing single-spa applications that provide a smooth user experience.

Advanced Topics

Using single-spa with different routing libraries

single-spa’s core routing mechanism is based on the browser’s URL. However, you can integrate single-spa with other routing libraries, such as React Router, Angular Router, or Vue Router, within your individual microfrontends. single-spa itself doesn’t dictate or interfere with the internal routing of your microfrontends. The activeWhen function in registerApplication determines which microfrontend is active based on the URL, and the internal routing libraries handle navigation within each microfrontend. You might need to adjust how you handle navigation within each microfrontend to ensure smooth transitions between different parts of your application.

Handling errors and exceptions

Robust error handling is essential for a production-ready single-spa application. You should implement error handling at multiple levels:

Integrating with other libraries and tools

single-spa is designed to work well with various libraries and tools. You can integrate it with:

The key is to ensure that these integrations are done within the individual microfrontends, keeping them independent and loosely coupled.

Testing your application

Testing a single-spa application requires a multi-faceted approach:

Debugging and troubleshooting

Debugging a single-spa application can be challenging due to its distributed nature. Useful techniques include:

Security considerations

Security is paramount. Consider the following:

Migrating from other microfrontend approaches

Migrating from other microfrontend approaches (e.g., iframes, manual JavaScript loading) to single-spa involves refactoring your existing code to fit the single-spa lifecycle model. This includes creating the bootstrap, mount, and unmount functions for each of your applications, and configuring the single-spa router. The complexity depends on the existing architecture. Start with a small, self-contained component, migrate it to single-spa, and gradually migrate the remaining components.

Building a single-spa application with a monorepo

A monorepo (a single repository containing multiple projects) can simplify the development and management of a single-spa application. Tools like Nx, Turborepo, or Lerna can help manage the monorepo. The key is to configure your build process (e.g., using workspaces in Webpack or similar features in other build tools) to build each microfrontend independently within the monorepo. This enables efficient dependency management and shared code between your microfrontends.

Community and Support

Contributing to single-spa

We welcome contributions to single-spa! If you find a bug, have a feature request, or want to improve the documentation, please follow these steps:

  1. Report issues: Use the issue tracker on the official single-spa GitHub repository to report bugs or request new features. Provide clear and concise descriptions of the problem, including steps to reproduce it.

  2. Submit pull requests: Fork the single-spa repository, make your changes, and submit a pull request. Ensure your code follows the existing coding style and includes thorough tests. Clearly describe the changes in your pull request.

  3. Improve documentation: If you find the documentation unclear or missing information, submit a pull request to improve it. Clear, concise, and well-organized documentation is essential for the success of any open-source project.

  4. Follow the contribution guidelines: Review the contribution guidelines in the single-spa repository for details on code style, testing, and other requirements.

Community forums and support channels

The single-spa community is active and helpful. Here are some places to get support:

Frequently asked questions (FAQ)

This section will be updated periodically with answers to commonly asked questions. In the meantime, check the single-spa documentation for comprehensive information. Common questions often revolve around:

Troubleshooting common issues

Here are some common issues encountered when working with single-spa and potential solutions:

If you encounter issues not covered here, refer to the single-spa documentation or the community forums for assistance. Remember to provide as much detail as possible when reporting problems, including your setup, code snippets, and error messages.

Appendix

Glossary of terms

API reference

This section provides a detailed reference of the single-spa API. It includes:

Refer to the official single-spa documentation for the most up-to-date and complete API reference.

Examples and use cases

This section contains examples showcasing various aspects of single-spa, including:

Migrating from previous versions

This section details how to migrate your single-spa application from older versions to the latest version. This includes:

Always refer to the official release notes for the most accurate information on upgrading your single-spa application. Carefully review any breaking changes before upgrading to avoid unexpected problems. Consider testing thoroughly after upgrading to ensure everything functions as expected.