Building single-spa applications
A single-spa registered application is everything that a normal SPA is, except that it doesn't have an HTML page. In a single-spa world, your SPA contains many registered applications, where each has its own framework. Registered applications have their own client-side routing and their own frameworks/libraries. They render their own HTML and have full freedom to do whatever they want, whenever they are mounted. The concept of being mounted refers to whether a registered application is putting content on the DOM or not. What determines if a registered application is mounted is its activity function. Whenever a registered application is not mounted, it should remain completely dormant until mounted.
#
Creating a registered applicationTo create a registered application, first register the application with single-spa. Once registered, the registered application must correctly implement all of the following lifecycle functions inside of its main entry point.
#
Registered application lifecycleDuring the course of a single-spa page, registered applications are loaded, bootstrapped (initialized), mounted, unmounted, and unloaded. single-spa provides hooks into each phase via lifecycles
.
A lifecycle function is a function or array of functions that single-spa will call on a registered application. single-spa calls these by finding specific named exports from the registered application's main file.
Notes:
- Implementing
bootstrap
,mount
, andunmount
is required. But implementingunload
is optional. - Each lifecycle function must either return a
Promise
or be anasync function
. - If an array of functions is exported (instead of just one function), the functions will be called one-after-the-other, waiting for the resolution of one function's promise before calling the next.
- If single-spa is not started, applications will be loaded, but will not be bootstrapped, mounted or unmounted.
info
Framework-specific helper libraries exist in the single-spa ecosystem to implement these required lifecycle methods. This documentation is helpful for understanding what those helpers are doing, or for implementing your own.
#
Lifecyle propsLifecycle functions are called with a props
argument, which is an object with some guaranteed information and additional custom information.
#
Built-in propsEach lifecycle function is guaranteed to be called with the following props:
name
: The string name that was registered to single-spa.singleSpa
: A reference to the singleSpa instance, itself. This is intended to allow applications and helper libraries to call singleSpa APIs without having to import it. This is useful in situations where there are multiple webpack configs that are not set up to ensure that only one instance of singleSpa is loaded.mountParcel
: The mountParcel function.
#
Custom propsIn addition to the built-in props that are provided by single-spa, you may optionally specify custom props to be passed to an application. These customProps will be passed into each lifecycle method. The custom props are an object, and you can provide either the object or a function that returns the object. Custom prop functions are called with the application name and current window.location as arguments.
root-config.js
app1.js
Some use cases could be to:
- share a common access token with all child apps
- pass down some initialization information, like the rendering target
- pass a reference to a common event bus so each app may talk to each other
Note that when no customProps are provided during registration, props.customProps
defaults to an empty object.
#
Lifecycle helpersSome helper libraries that implement lifecycle functions for ease of use are available for many popular frameworks/libraries. Learn more on the Ecosystem page.
#
LoadWhen registered applications are being lazily loaded, this refers to when the code for a registered application is fetched from the server and executed. This will happen once the registered application's activity function returns a truthy value for the first time. It is best practice to do as little as possible / nothing at all during load
, but instead to wait until the bootstrap lifecycle function to do anything. If you need to do something during load
, simply put the code into a registered application's main entry point, but not inside of an exported function. For example:
#
BootstrapThis lifecycle function will be called once, right before the registered application is mounted for the first time.
#
MountThis lifecycle function will be called whenever the registered application is not mounted, but its activity function returns a truthy value. When called, this function should look at the URL to determine the active route and then create DOM elements, DOM event listeners, etc. to render content to the user. Any subsequent routing events (such as hashchange
and popstate
) will not trigger more calls to mount
, but instead should be handled by the application itself.
#
UnmountThis lifecycle function will be called whenever the registered application is mounted, but its activity function returns a falsy value. When called, this function should clean up all DOM elements, DOM event listeners, leaked memory, globals, observable subscriptions, etc. that were created at any point when the registered application was mounted.
#
UnloadThe unload
lifecycle is an optionally implemented lifecycle function. It will be called whenever an application should be unloaded
. This will not ever happen unless someone calls the unloadApplication
API. If a registered application does not implement the unload lifecycle, then it assumed that unloading the app is a no-op.
The purpose of the unload
lifecycle is to perform logic right before a single-spa application is unloaded. Once the application is unloaded, the application status will be NOT_LOADED and the application will be re-bootstrapped.
The motivation for unload
was to implement the hot-loading of entire registered applications, but it is useful in other scenarios as well when you want to re-bootstrap applications, but perform some logic before applications are re-bootstrapped.
#
TimeoutsBy default, registered applications obey the global timeout configuration, but can override that behavior for their specific application. This is done by exporting a timeouts
object from the main entry point of the registered application. Example:
app-1.js
Note that millis
refers to the number of milliseconds for the final console warning, and warningMillis
refers to the number of milliseconds at which a warning will be printed to the console (on an interval) leading up to the final console warning.
#
Transitioning between applicationsIf you find yourself wanting to add transitions as applications are mounted and unmounted, then you'll probably want to tie into the bootstrap
, mount
, and unmount
lifecycle methods. This single-spa transitions repo is a small proof-of-concept of how you can tie into these lifecycle methods to add transitions as your apps mount and unmount.
Transitions for pages within a mounted application can be handled entirely by the application itself. For example, using react-transition-group for React-based projects.