Sign in

Picture sourced from Google Images

How to run Angular & React together in one UI app using single-spa

single-spa is a framework for bringing together multiple JavaScript microfrontends in a frontend application. It helps in building web UI applicatons using multiple frameworks on the same page without page refreshing (React, -AngularJS, Angular, Ember, or whatever you’re using). This article will help developers integrate Angular & ReactJS together in one single app.

JavaScript is the world’s most misunderstood programming language. — Douglas Crockford, JS Architect & inventor of JSON, JSMint & JSLint.

Prerequisite :
This article will assume the reader has a basic knowledge of npm & node, has a basic understanding of how Javascript works. Also has worked on implementing &/or developing any Javascript-based UI frameworks like Angular, ReactJS, Vue, Ember, etc. Also please make sure your webpack, webpack-dev-server, node, npm & other related dependencies are up to date.

1. Install single-spa CLI, create Angular & ReactJS projects using the CLI

Single-spa CLI will help in autogenerating managed configurations for webpack, babel, jest, etc. CLI is recommended when starting a new UI app either in single-spa or any other frameworks as it helps in setting up the basic tooling & config for your app. We will be installing the CLI globally.

npm install --global create-single-spa

We have to initialize three projects for our single-spa app to run properly :
- an Angular project
- a ReactJS project
- one root-config JS project (this will help in integrating both the react & angular together)
After the CLI has been installed, please run it. It will generate a new project structure for the react app.

create-single-spa --framework react

The CLI is very helpful & self-explanatory.
Choose single-spa application/parcel & hit enter/return.
Skip other options & add org name when asked.
It will ask for config options & input. You can select the default ones for the config related stuff. For selecting the default ones just keep hitting the return/enter key. It will also run npm install for you.

Run your ReactJS application.

npm run start

Now we have our ReactJS application ready. We will create a new Angular application.

Run the Angular CLI (ng) with routing & prefix. The Prefix is important if we have multiple Angular apps, their component selectors won't have the same name.

ng new my-angular-app --routing --prefix my-angular-app
cd my-angular-app

Now add single-spa config to angular project

ng add single-spa-angular

Next, go to your app-routing-module.ts and add a provider for your APP_BASE_HREF. We will discuss the significance of APP_BASE_HREF in a single-spa angular applicaton later.

providers: [{ provide: APP_BASE_HREF, useValue: '/' }]

Make sure you have the ** route specified for EmptyRouteComponent

{ path: '**', component: EmptyRouteComponent }

Make sure you have the following build script inside your package.json

"serve:single-spa:my-angular-app": "npm install ng s --project my-angular-app --disable-host-check --port 4200 --deploy-url http://localhost:4200/ --live-reload false

Run the angular app

npm run serve:single-spa:my-angular-app

2. Create root-config project

The root config exists only to start up the single-spa applications. The single-spa root config consists of :
- HTML file that is shared by all single-spa applications.
- JS file which calls singleSpa.registerApplication().

The JS file will help to register & mount the applications as required. We are going to use import-maps to import react & angular components in our single spa root config html.

npx create-single-spa root-config

After running the command choose the last option to create root-config, skip the other config by hitting enter/return.

3. Configure root-config

Navigate to index.ejs inside src & make the following changes :
1. Search for <% if (isLocal) { %> <script type=”systemjs-importmap”>
2. You will see the imports JSON inside the scriplet, add the imports from Angular & ReactJs apps that are running on your system.
The following is an example how it looks after it has been added.

<% if (isLocal) { %>
<script type="systemjs-importmap">
{
"imports": {
"react":"https://cdn.jsdelivr.net/npm/react@16.13.1/umd/react.devel opment.js",
"react-dom":"https://cdn.jsdelivr.net/npm/react-dom@16.13.1/umd/react-dom.development.js",
"@abhi/root-config": "//localhost:9000/abhi-root-config.js",
"@abhi/single-spa-react": "//localhost:8080/abhi-single-spa-angular-react.js",
"@abhi/my-angular-app": "http://localhost:4200/main.js",
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.5.0/lib/system/single-spa.min.js"
}
}
</script>
<% } %>

You need to make sure that react, react-dom, single-spa are also present inside the imports json. There should be total of 6 imports inside the json. Lets double check the port numbers for root-config, single-spa-react, my-angular-app if they are correct.

3. Remove the comment against the zone.js import.

<script src="https://cdn.jsdelivr.net/npm/zone.js@0.10.3/dist/zone.min.js"></script>

4. go to js file inside the src & add the following function

function loadWithoutAmd(name) {
return Promise.resolve().then(() => {
let globalDefine = window.define;
delete window.define;
return System.import(name).then((module) => {
window.define = globalDefine;
return module;
});
});
}

5. Add register code for ReactJs & Angular.
registerApplication takes name, system import name & others as params. Make sure the names match with whatever declared previously in index.ejs & package.json of respective apps running.(ReactJs & Angular in our case)

registerApplication(
"@abhi/single-spa-react",
() => System.import("@abhi/single-spa-angular-react"),
true
);
registerApplication(
"@abhi/my-angular-app",
() => System.import("@abhi/my-angular-app"),
true
);

6. Make sure your start function is declared properly.

start({
urlRerouteOnly: true,
});

7. Final js file should look somewhat like this.

import { registerApplication, start } from "single-spa";registerApplication(
"@abhi/single-spa-react",
() => System.import("@abhi/single-spa-react"),
true
);
registerApplication(
"@abhi/my-angular-app",
() => System.import("@abhi/my-angular-app"),
true
);
start({
urlRerouteOnly: true,
});
function loadWithoutAmd(name) {
return Promise.resolve().then(() => {
let globalDefine = window.define;
delete window.define;
return System.import(name).then((module) => {
window.define = globalDefine;
return module;
});
});
}

Finally, all our configurations are ready, execute npm run start inside the root-config folder.
Open your browser and hit http://localhost:9000

Your Angular & react content should get redered.

Note that both components may not be getting rendered in any order. It depends how system js imports the js files using import-maps and which js file loads early in the index.ejs inside root-config.

Developer & Coder. Full time geek & part time human. I tend to ponder on Cloud Tech, Fin Tech, abstract algos & discrete maths.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store