Micro-Frontends with Glimmer.js and single-spa
Tags: microfrontends, glimmerjs, singlespa
Published: 2021-01-12 10:34:51

In this post, I am going to walk you through on how to create Micro-Frontends with [Glimmer.js](https://glimmerjs.com) using the [single-spa](https://single-spa.js.org) library.

We are going to create a simple application like a Starwars wiki with just only two pages for the list of planets and people references in Star Wars movie. The planets page will list the planets from the films and their details. Similarly the people page will list the characters from the movies and their details. 

You can view the Glimmer Micro-frontends in action [here](https://glimmer-mf.surge.sh) and the source code is hosted in this [github org](https://github.com/glimmer-mfe).

Before diving into the tutorial, let's make ourselves clear about the tools and technologies we are going to use.

## Micro-Frontends
[Micro-Frontends](https://micro-frontends.org/) is about extending the micro-service idea to frontend development. It is a collection of techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently.

## Glimmer.js
[Glimmer](https://glimmerjs.com) is one of the fastest DOM rendering engines, delivering exceptional performance for initial renders as well as updates. Architected like a virtual machine (VM), Glimmer compiles your templates into low-level code so it can run as fast as possible—without sacrificing ease of use. Glimmer VM powers the components in [Ember.js](https://emberjs.com)

## single-spa
[single-spa](https://single-spa.js.org) is a JavaScript router for front-end microservices. With single-spa you can use multiple frameworks in a single-page application, allowing you to split code by functionality and have Angular, React, Vue.js, etc. apps all living in harmony. single-spa makes them work together and won't load them until they're needed.

## single-spa-glimmer
[single-spa-glimmer](https://github.com/rajasegar/single-spa-glimmer) is a helper library that helps implement single-spa registered application lifecycle functions (bootstrap, mount and unmount) for for use with Glimmer. For more information, check out the github repository.

One important thing we are doing inside this library, especially inside the `mount` lifecycle hook is the below:

```js
function mount(opts) {
  return Promise
    .resolve()
    .then(() => {
      // Turning off validator registration for allowing multiple glimmer apps in same page
      globalThis[Symbol.for('GLIMMER_VALIDATOR_REGISTRATION')] = false;
      const { renderComponent, App, root, owner } = opts;
      const element = document.getElementById(root);
      renderComponent(App, { 
        element,
        owner,
      });
    });
}
```

We are deliberately turning off the [@glimmer/validator](https://github.com/glimmerjs/glimmer-vm/blob/2a8cb9642fbe8e548dffe8c462686d8b1d888fbf/packages/%40glimmer/validator/index.ts#L7) registration, to allow multiple Glimmer apps co-exist within a single HTML page. Currently, this is not possible, but thanks to the above workaround proposed by [John Griffin](https://github.com/john-griffin) in this [issue](https://github.com/glimmerjs/glimmer-vm/issues/1252) he had created in the [glimmer-vm](https://github.com/glimmerjs/glimmer-vm) repository.

## Recommended Setup by single-spa core team
The single-spa core team recommends a default setup to create micro-frontends with any framework based on a set of conventions which will help us to scale our micro frontends in the long run.

We are going to follow the below setup:
```
glimmer-micro-frontends
|- root-config
|- navbar
|- people
|- planets

```
### root-config
This is the [host app](https://github.com/glimmer-mfe/root-config) that manages all the other micro frontends using single-spa. 

Let's see how to create the root-config app using the CLI:
Invoke the `create-single-spa` command at the terminal
```
create-single-spa
```
It will present you with a set of options like directory, type of application. With create-single-spa you can create different type of applications like single-spa-application, in-browser utility and root-config. You can read more about the different type of modules [here](https://single-spa.js.org/docs/module-types).

For this project you have to choose the `single-spa root config` option and follow the other successive prompts.

```
? Directory for new project 
? Select type to generate (Use arrow keys)
  single-spa application / parcel
  in-browser utility module (styleguide, api cache, etc)
❯ single-spa root config
```
Once the generators are done, you will have a `src` folder with two files. `index.ejs` is the html template for our host app where we will have the placeholders for the other micro frontends. 



```html
 <template id="single-spa-layout">
    <single-spa-router>
        <application name="@glimmer-mf/navbar"></application>
        <div id="navbar"></div>
      <main>
        <route default>
        <p class="mfe-info">
        This example project shows independently built and deployed microfrontends that use <a href="https://glimmerjs.com">Glimmer.js</a> and <a href="https://single-spa.js.org">single-spa</a>. Each nav link above takes you to a different microfrontend.
        </p>
        </route>
        <route path="people">
        <application name="@glimmer-mf/people"></application>
        </route>
        <route path="planets">
        <application name="@glimmer-mf/planets"></application>
        </route>
      </main>
      <footer>
        <p><a href="https://github.com/glimmer-mfe">Github</a></p>
      </footer>
    </single-spa-router>
  </template>

```
And the next file will be `root-config.js`. This is where we will register all our Glimmer micro frontends using the single-spa `registerApplication`

We are making use of the [single-spa-layout](https://github.com/single-spa/single-spa-layout/) engine here. The layout engine provides a routing API that controls your top level routes, applications, and DOM elements. Using single-spa-layout makes it easier to accomplish the following:

- DOM placement and ordering of applications.
- Loading UIs when applications are downloaded.
- Default routes for Not Found / 404 pages.
- Server side rendering of single-spa applications
- Error pages

To know more about the layout engine you can take a look at the [official documentation](https://single-spa.js.org/docs/layout-overview).

```js
import { registerApplication, start } from "single-spa";

import {
  constructApplications,
  constructRoutes,
  constructLayoutEngine,
} from "single-spa-layout";
const routes = constructRoutes(document.querySelector("#single-spa-layout"));
const applications = constructApplications({
  routes,
  loadApp({ name }) {
    return System.import(name);
  },
});
const layoutEngine = constructLayoutEngine({ routes, applications });
applications.forEach(registerApplication);
start();

```


### navbar
This [app](https://github.com/glimmer-mfe/navbar) contains a navigation bar to route to the different micro-frontends based on the url.

Create the navigation markup in `src/App.js` component.

```html
 <nav id="global-nav" {{startRouting}}>
    <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/people">People</a></li>
    <li><a href="/planets">Planets</a></li>
    </ul>
 </nav>

```

And finally we need to hook up the lifecycles for the Glimmer app with the single-spa library in `src/index.js`. We need to give the `single-spa-glimmer` function the parent Component, the application owner object with the list of services that needs to be injected, the `renderComponent` function from `@glimmer/core` and the DOM element id for mounting the parent component. The id will be the same as the application name we gave in the root-config markup earlier. 

For example, if the application name is `@glimmer-mf/people` the DOM element id will be `single-spa-application:@glimmer-mf/people` which will be automatically created and added to the DOM by the single-spa layout engine, whenever the `/people` url is visited in the app.


```js
import { renderComponent } from '@glimmer/core';
import App from './App.js';
import LocaleService from './services/LocaleService.js';
import RouterService from './services/RouterService.js';
import singleSpaGlimmer from './single-spa-glimmer.js';

const root = 'single-spa-application:@glimmer-mf/people';
const owner = {
  services: {
    locale: new LocaleService('en_US'),
    router: new RouterService()
  }
};
const glimmerLifecycles = singleSpaGlimmer({
  App,
  renderComponent,
  root,
  owner
});

export const bootstrap = glimmerLifecycles.bootstrap;
export const mount = glimmerLifecycles.mount;
export const unmount = glimmerLifecycles.unmount;
```

We have to do the above step for each Glimmer app we create.

### people
This [micro frontend](https://github.com/glimmer-mfe/people) is for the people page and list the people and their details using the Starwars API [SWAPI](https://swapi.tech/).

I am not going to discuss in depth about the functional code to fetch the api and show the list of people in this app. That has already been discussed in my previous blog post here.

{% post https://dev.to/rajasegar/ember-micro-frontends-with-single-spa-1p8j %}

Instead, we will take a look at the `webpack.config.js` for the Glimmer apps, because we need to tweak it a little bit, to build for single-spa compatibility.

```js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const path = require('path');

module.exports = (env) => {
  return {
  mode: env.production ? 'production' : 'development',
  entry: './src/index.js',
  output: {
    filename: 'people.js',
    path: path.resolve(__dirname, 'dist'),
    libraryTarget: 'system',
    publicPath: env.production ? 'https://glimmer-mf-people.surge.sh/' : '//localhost:8081/'
  },
  devtool:'inline-source-map',
  plugins: [
    new CleanWebpackPlugin(),
  ],
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            plugins:[ 
              ['@glimmer/babel-plugin-glimmer-env', { DEBUG: true}],
              '@glimmer/babel-plugin-strict-template-precompile',
              ['@babel/plugin-proposal-decorators', { legacy: true }],
              '@babel/plugin-proposal-class-properties',
            ],
            presets: [['@babel/preset-env', { targets: { esmodules: true } } ]],
          },
        },
      },
    ],
  },
  devServer: {
    headers: {
      "Access-Control-Allow-Origin": "*",
    }
  }
};
}
```

The significant thing to notice in the config is the `libraryTarget` property which is assigned the value `system` to make the build compatible with single-spa, since it uses `System.js` under the hood. You can read more about the recommended [build tools](https://single-spa.js.org/docs/recommended-setup#build-tools-webpack--rollup) config for different bundlers like Webpack and Rollup.

I have made a sample repo to re-use for single-spa in Glimmer with webpack, so that you don't have to start from scratch for creating Glimmer Micro-Frontends with single-spa.

{% github rajasegar/glimmer-sspa %}

This is how the people app will look like in action.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/cjk6kayv98256p5xdxk8.jpg)

### planets
The [planets](https://github.com/glimmer-mfe/planets) micro frontend is almost similar to the people micro frontend except it uses the planets api from SWAPI.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ryxtt2azzw53gn4q0odu.jpg)

## Running the app
Once you have done all the above things properly, you can start the Glimmer microfrontends and see them in action in the local environment by firing up `yarn start` from the `root-config` app.
```
yarn start
```
This will start the `webpack-dev-server` in the port 9000 by booting up the app and you can view the app in `localhost:9000`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/gznhyx65v0hfmj48aai2.jpg)

## What's next?
We still have a long way to go in this approach of using Glimmer to create micro front-ends. There are some challenges with respect to routing within the apps which we need to address. 

This is an entirely new approach of creating Micro-Frontends using Glimmer.js for all the frontends. Since Glimmer is lightweight and fast, the performance benefits supersede all the other boilerplate code we need to have for routing and rendering components. 

You are not tied with a particular framework implementation, you are free to choose any framework to add to the micro frontends here with other Glimmer apps. 

You don't have to redeploy other Glimmer apps if you change your dependencies with respect to the other micro frontends, because single-spa registers your dependencies from where you have hosted the other micro-frontends.

You can dynamically mount/unmount you apps with the [single-spa-inspector](https://single-spa.js.org/docs/devtools) browser addon provided by the single-spa team in your browser. It supports Chrome and Firefox browsers.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/lpfbsnhydv3r9mm2743n.png)

You can also override the apps at runtime inside the browser with the addon for testing any micro-frontend in your local development environments.

Please let me know your thoughts in the comments section, about building Glimmer micro-frontends using single-spa and also check out single-spa for building micro-frontends in other frameworks like React, Vue, Svelte, Preact, etc.,

You can find the list of examples in this [page](https://single-spa.js.org/docs/examples)

## References:
- [single-spa](https://single-spa.js.org)
- [single-spa-glimmer](https://github.com/rajasegar/single-spa-glimmer)
- [Glimmer.js](https://glimmerjs.com)
- [glimmer-micro-frontends](https://glimmer-mf.surge.sh)
- [Github source code](https://github.com/glimmer-mfe)