A layout is a combination of HTMLElements, routes, and single-spa applications. Layout is defined statically in your root config to handle your top level routes and dom elements. Single-spa-layout should not be used outside of the root config; instead, a UI framework (React, Angular, Vue) should handle layouts within the applications.

You may define layouts as either HTML templates or JSON objects. Defining in JSON is supported for organizations who prefer storing their layout definitions in a database instead of code. Both HTML and JSON layouts have the same feature set. However, storing layouts in code is generally preferred and encouraged by default. If you’re just getting started with single-spa-layout, we encourage using an HTML template.

Once you define your layout, you should constructRoutes, constructApplications, and constructLayoutEngine.

HTML Layouts

You may define HTML layouts either within your root config’s index.html file, or within a javascript string that is parsed as HTML. We generally encourage defining the layout within your root config’s index.html file.

To define a layout within your index.html file, create a <template id="single-spa-layout"> element that contains your layout. Within the template, add a <single-spa-router> element, along with any routes, applications, and dom elements.

Note that HTMLElements defined in your layout are static - there is no way to forcibly re-render or change them.

  1. <!-- index.ejs -->
  2. <html>
  3. <head>
  4. <template>
  5. <single-spa-router>
  6. <div class="main-content">
  7. <route path="settings">
  8. <application name="settings"></application>
  9. </route>
  10. </div>
  11. </single-spa-router>
  12. </template>
  13. </head>
  14. </html>
  1. // Not recommended, but javascript construction of HTMLElements is also possible
  2. const doc = new DOMParser().parseFromString(`
  3. <single-spa-router>
  4. <div class="main-content">
  5. <route path="settings">
  6. <application name="settings"></application>
  7. </route>
  8. </div>
  9. </single-spa-router>
  10. `, "text/html").documentElement

JSON Layouts

You may define your layout as JSON, including routes, applications, and arbitrary dom elements.

  1. const layout = {
  2. "routes": [
  3. { "type": "route", "path": "settings", "routes": [
  4. { "type": "application", "name": "settings" }
  5. ]}
  6. ]
  7. };

Layout Elements

A layout element is an HTMLElement or JSON object that represents either a dom node, route, or application.

<template>

The template element is only used when defining the layout as HTML. Its purpose is to prevent its contents from being displayed by the browser, since the layout definition should not be visible to user.

  1. <template>
  2. <!-- Define your layout here -->
  3. <single-spa-router></single-spa-router>
  4. </template>

Note that <template> elements are not fully supported in IE11. However, you do not need to polyfill template elements in order to use them in single-spa-layout. Instead, simply add style="display: none;" to the template to prevent its contents from being displayed in IE11.

  1. <template style="display: none;">
  2. <!-- Define your layout here -->
  3. <single-spa-router></single-spa-router>
  4. </template>

<single-spa-router>

The single-spa-router element is required as the top level container of your layout. All attributes are optional.

  1. <single-spa-router mode="hash|history" base="/" disableWarnings></single-spa-router>
  1. {
  2. "mode": "hash|history",
  3. "base": "/",
  4. "disableWarnings": false,
  5. "containerEl": "#container",
  6. "routes": []
  7. }

Attributes

  • mode (optional): A string that must be hash or history that defaults to history. This indicates whether the routes should be matched against the Location pathname or hash.
  • base (optional): A string URL prefix that will be considered when matching route paths.
  • disableWarnings (optional): A boolean that turns of single-spa-layout’s console warnings when the elements provided are incorrect.
  • containerEl (optional): A string CSS Selector or HTMLElement that is used as the container for all single-spa dom elements. Defaults to body.

<route>

The route element is used to control which applications and dom elements are shown for a top-level URL route. It may contain HTMLElements, applications, or other routes.

  1. <route path="clients">
  2. <application name="clients"></application>
  3. </route>
  4. <route default>
  5. <application name="clients"></application>
  6. </route>
  1. {
  2. "type": "route",
  3. "path": "clients",
  4. "routes": [
  5. { "type": "application", "name": "clients" }
  6. ],
  7. "default": false
  8. }

Attributes

Routes must either have a path or be a default route.

  • routes (required): An array of children elements that will be displayed when the route is active
  • path (optional): A string path that will be matched against the browser’s URL. The path is relative to its parent route (or the base URL). Leading and trailing / characters are unnecessary and are automatically applied. Paths may contain “dynamic segments” by using the : character ("clients/:id/reports"). Single-spa-layout uses single-spa’s pathToActiveWhen function to convert the path string to an activity function.
  • default (optional): A boolean that determines whether this route will match all remaining URLs that have not been defined by sibling routes. This is useful for 404 Not Found pages. A sibling route is defined as any route with the same nearest-parent-route.
  • props: An object of single-spa custom props that will be provided to the application when it is mounted. Note that these can be defined differently for the same application on different routes. You can read more about defining props within your HTML in the docs below.

<application>

The application element is used to render a single-spa application. Applications may be contained within route elements, or may exist at the top level as applications that will always be rendered. A container HTMLElement will be created by single-spa-layout when the application is rendered. The container HTMLElement is created with an id attribute of single-spa-application:appName such that your framework helpers will automatically use it when mounting the application.

The same application may appear multiple times in your layout, under different routes. However, each application can only be defined once per-route.

  1. <!-- Basic usage -->
  2. <application name="appName"></application>
  3. <!-- Use a named loader that is defined in javascript -->
  4. <application name="appName" loader="mainContentLoader"></application>
  5. <!-- Add single-spa custom props to the application. The value of the prop is defined in javascript -->
  6. <application name="appName" props="myProp,authToken"></application>
  1. // Basic usage
  2. {
  3. "type": "application",
  4. "name": "appName"
  5. }
  6. // Use a single-spa parcel as a loading UI
  7. // You may also use Angular, Vue, etc.
  8. const parcelConfig = singleSpaReact({...})
  9. {
  10. "type": "application",
  11. "name": "appName",
  12. "loader": parcelConfig
  13. }
  14. // Use an HTML string as a loading UI
  15. {
  16. "type": "application",
  17. "name": "appName",
  18. "loader": "<img src='loading.gif'>"
  19. }
  20. // Add single-spa custom props
  21. {
  22. "type": "application",
  23. "name": "appName",
  24. "props": {
  25. "myProp": "some-value"
  26. }
  27. }

Attributes

DOM elements

Arbitrary HTMLElements may be placed anywhere in your layout. To do so in HTML, simply add the HTMLElemet like normal. Defining HTMLElements in JSON is not supported yet, but will be soon tracking issue.

  1. <nav class="topnav"></nav>
  2. <div class="main-content">
  3. <button>A button</button>
  4. </div>

single-spa-layout only supports updating DOM elements during route transitions. Arbitrary re-renders and updates are not supported.

DOM elements defined within a route will be mounted/unmounted as the route becomes active/inactive. If you define the same DOM element twice under different routes, it will be destroyed and recreated when navigating between the routes.

Props

Single-spa custom props may be defined on both route and application elements. Any route props will be merged together with the application props to create the final props that are passed to the single-spa lifecycle functions.

JSON

In a JSON layout definition, you can define props with the props property on your applications and routes:

  1. import { constructRoutes } from 'single-spa-layout';
  2. constructRoutes({
  3. routes: [
  4. { type: "application", name: "nav" props: { title: "Title" } },
  5. { type: "route", path: "settings" props: { otherProp: "Some value" } },
  6. ]
  7. })

HTML

Defining props on JSON objects is straightforward, as they are an object that can contain strings, numbers, booleans, objects, arrays, etc. However, defining complex data types in HTML is not as straightforward, since HTML attributes are always strings. To work around this, single-spa-layout allows you to name your props in the HTML, but define their values in javascript.

  1. <application name="settings" props="authToken,loggedInUser"></application>
  1. import { constructRoutes } from 'single-spa-layout';
  2. const data = {
  3. props: {
  4. authToken: "fds789dsfyuiosodusfd",
  5. loggedInUser: fetch('/api/logged-in-user').then(r => r.json())
  6. }
  7. }
  8. const routes = constructRoutes(document.querySelector('#single-spa-template'), data)

The full API documentation for the constructRoutes API explains the data object in detail.

Loading UIs

It is often desireable to show a loading UI when waiting for an application’s code to download and execute. Single-spa-layout allows you to define per-application loaders that will be mounted to the DOM while the application’s loading function is pending. It is possible to share the same loading UI for multiple applications.

A loading UI is defined as either an HTML string or as a parcel config object. HTML strings are best for static, non-interactive loaders, whereas parcels are best when you want to use a framework (Vue, React, Angular, etc) to dynamically render the loader.

Defining loaders via javascript objects is straightforward, as they are an object that can contain strings, numbers, booleans, objects, arrays, etc. However, defining complex data types in HTML is not as straightforward, since HTML attributes are always strings. To work around this, single-spa-layout allows you to name your loaders in the HTML, but define their values in javascript.

  1. <application name="topnav" loader="topNav"></application>
  2. <application name="topnav" loader="settings"></application>
  1. import { constructRoutes } from 'single-spa-layout';
  2. // You may also use Angular, Vue, etc.
  3. const settingsLoader = singleSpaReact({...})
  4. const data = {
  5. loaders: {
  6. topNav: `<nav class="placeholder"></nav>`,
  7. settings: settingsLoader
  8. }
  9. }
  10. const routes = constructRoutes(document.querySelector('#single-spa-template'), data)

The full API documentation for the constructRoutes API explains the data object in detail.

Transitions

Support for route transitions is planned, but not yet implemented. If you have interest in this feature, please provide use cases, upvotes, and feedback in this tracking issue.

Default Routes (404 Not Found)

Default routes are routes that activate when no other sibling routes match the current URL. They do not have a URL path and may contain any combination of DOM elements and single-spa applications.

  1. <single-spa-router>
  2. <route path="cart"></route>
  3. <route path="product-detail"></route>
  4. <route default>
  5. <h1>404 Not Found</h1>
  6. </route>
  7. </single-spa-router>

Default routes are matched against their sibling routes, which allows for nesting:

  1. <single-spa-router>
  2. <route path="cart"></route>
  3. <route path="product-detail/:productId">
  4. <route path="reviews"></route>
  5. <route path="images"></route>
  6. <route default>
  7. <h1>Unknown product page</h1>
  8. </route>
  9. </route>
  10. <route default>
  11. <h1>404 Not Found</h1>
  12. </route>
  13. </single-spa-router>

Sibling routes are defined as those that share a “nearest parent route.” This means that they do not have to be direct siblings in your HTML/JSON, but can be nested within DOM elements:

  1. <single-spa-router>
  2. <route path="product-detail/:productId">
  3. <div class="product-content">
  4. <route path="reviews"></route>
  5. <route path="images"></route>
  6. </div>
  7. <!-- The reviews and images routes are siblings, since they share a nearest parent route -->
  8. <!-- The default route will activate when the URL does not match reviews or images -->
  9. <route default>
  10. <h1>Unknown product page</h1>
  11. </route>
  12. </route>
  13. </single-spa-router>