Building Micro Frontends with React, Vue, and Single-spa
Tags: vue, react, javascript, microfrontends
Published: 2019-06-18 19:51:27

---
title: Building Micro Frontends with React, Vue, and Single-spa
published: true
description: How to use multiple JavaScript frameworks in a single-page application.
tags: vue, react, javascript, micro-frontends
---

![](https://thepracticaldev.s3.amazonaws.com/i/wstpdc9ie6iw77qfeam7.jpg)

Over the past few weeks the there has been a ton of discussion around micro-frontends (some negative, some positive).

There was one tweet that really caught my eye from [Joel Denning ‏](https://twitter.com/Joelbdenning/status/1139417020347240448), the creator of [Single SPA](https://single-spa.js.org/):

{% twitter 1139417020347240448 %}

When I see something new and controversial like this, I always want to try it out myself to see what all of the hype is about and also so I can form my own opinions about the subject.

This lead me down the path to creating a micro-frontend application that rendered two separate React applications along with a single Vue application.

In this tutorial I'll share what I've learned and show you how to build a micro-frontend app consisting of a React and a Vue application. 

> To view the final code for this application, click [here](https://github.com/dabit3/micro-frontend-example).

## Single SPA

The tool we will be using to create our project is [Single SPA](https://single-spa.js.org/) - A javascript framework for front-end microservices.

Single SPA enables you to 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 together.

You may be used to the days of the Create React APP CLI and the Vue CLI. With these tools you can quickly spin up an entire project, complete with webpack configurations, dependencies, and boilerplate ready to go for you.

If you're used to this ease of setup, then this first part may be somewhat jarring. That is because we will be creating everything from scratch, including installing all of the dependencies we need as well as creating the webpack and babel configuration from scratch.

> If you are still curious what Single SPA does or why you may want to build using a micro-frontend architecture, check out [this](https://www.youtube.com/watch?v=L4jqow7NTVg&feature=youtu.be) video.

## Getting Started

The first thing you'll need to do is create a new folder to hold the application and change into the directory:

```sh
mkdir single-spa-app

cd single-spa-app
```

Next, we'll initialize a new package.json file:

```sh
npm init -y
```

Now, _this is the fun part_. We will install all of the dependencies that we will need for this project. I will split these up into separate steps.

#### Installing regular dependencies

```sh
npm install react react-dom single-spa single-spa-react single-spa-vue vue
```

#### Installing babel dependencies

```sh
npm install @babel/core @babel/plugin-proposal-object-rest-spread @babel/plugin-syntax-dynamic-import @babel/preset-env @babel/preset-react babel-loader --save-dev
```

#### Installing webpack dependencies

```sh
npm install webpack webpack-cli webpack-dev-server clean-webpack-plugin css-loader html-loader style-loader vue-loader vue-template-compiler --save-dev
```

Now, all of the dependencies have been installed and we can create our folder structure.

The main code of our app will live in a __src__ directory. This __src__ directory will hold subfolders for each of our applications. Let's go ahead and create the __react__ and __vue__ application folders within the __src__ folder:

```sh
mkdir src src/vue src/react
```

Now, we can create the configuration for both webpack and babel.

#### Creating webpack configuration

In the root of the main application, create a `webpack.config.js` file with the following code:

```javascript
const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const VueLoaderPlugin = require('vue-loader/lib/plugin')

module.exports = {
  mode: 'development',
  entry: {
    'single-spa.config': './single-spa.config.js',
  },
  output: {
    publicPath: '/dist/',
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist'),
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      }, {
        test: /\.js$/,
        exclude: [path.resolve(__dirname, 'node_modules')],
        loader: 'babel-loader',
      },
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      }
    ],
  },
  node: {
    fs: 'empty'
  },
  resolve: {
    alias: {
      vue: 'vue/dist/vue.js'
    },
    modules: [path.resolve(__dirname, 'node_modules')],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new VueLoaderPlugin()
  ],
  devtool: 'source-map',
  externals: [],
  devServer: {
    historyApiFallback: true
  }
};

```

#### Creating babel configuration

In the root of the main application, create a `.babelrc` file with the following code:

```json
{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "browsers": ["last 2 versions"]
      }
    }],
    ["@babel/preset-react"]
  ],
  "plugins": [
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-proposal-object-rest-spread"
  ]
}

```

#### Initializing Single-spa 

Registering applications is how we tell __single-spa__ when and how to bootstrap, mount, and unmount an application.

In the `webpack.config.js` file we set the entry point to be `single-spa.config.js`.

Let's go ahead and create that file in the root of the project and configure it.

#### single-spa.config.js

```javascript
import { registerApplication, start } from 'single-spa'

registerApplication(
  'vue', 
  () => import('./src/vue/vue.app.js'),
  () => location.pathname === "/react" ? false : true
);

registerApplication(
  'react',
  () => import('./src/react/main.app.js'),
  () => location.pathname === "/vue"  ? false : true
);

start();
```

This file is where you register all of the applications that will be part of the main single page app. Each call to `registerApplication` registers a new application and takes three arguments:

1. App name
2. Loading function (what entrypoint to load)
3. Activity function (logic to tell whether to load the app)

Next, we need to create the code for each of our apps.

### React app

In __src/react__, create the following two files:

```sh
touch main.app.js root.component.js
```

#### src/react/main.app.js

```javascript
import React from 'react';
import ReactDOM from 'react-dom';
import singleSpaReact from 'single-spa-react';
import Home from './root.component.js';

function domElementGetter() {
  return document.getElementById("react")
}

const reactLifecycles = singleSpaReact({
  React,
  ReactDOM,
  rootComponent: Home,
  domElementGetter,
})

export const bootstrap = [
  reactLifecycles.bootstrap,
];

export const mount = [
  reactLifecycles.mount,
];

export const unmount = [
  reactLifecycles.unmount,
];
```

#### src/react/root.component.js

```javascript
import React from "react"

const App = () => <h1>Hello from React</h1>

export default App
```

### Vue app

In __src/vue__, create the following two files:

```sh
touch vue.app.js main.vue
```

#### src/vue/vue.app.js

```javascript
import Vue from 'vue';
import singleSpaVue from 'single-spa-vue';
import Hello from './main.vue'

const vueLifecycles = singleSpaVue({
  Vue,
  appOptions: {
    el: '#vue',
    render: r => r(Hello)
  } 
});

export const bootstrap = [
  vueLifecycles.bootstrap,
];

export const mount = [
  vueLifecycles.mount,
];

export const unmount = [
  vueLifecycles.unmount,
];
```

#### src/vue/main.vue

```javascript
<template>
  <div>
      <h1>Hello from Vue</h1>
  </div>
</template>
```

Next, create the index.html file in the root of the app:

```sh
touch index.html
```

#### index.html

```html
<html>
  <body>
    <div id="react"></div>
    <div id="vue"></div>
    <script src="/dist/single-spa.config.js"></script>
  </body>
</html>
```

### Updating Package.json with scripts

To run the app, let's add the start script as well as a build script in package.json:

```json
"scripts": {
  "start": "webpack-dev-server --open",
  "build": "webpack --config webpack.config.js -p"
}
```

### Running the app

To run the app, run the `start` script:

```sh
npm start
```

Now, you can visit the following URLs:

```sh
# renders both apps
http://localhost:8080/

# renders only react
http://localhost:8080/react

# renders only vue
http://localhost:8080/vue
```

> To view the final code for this application, click [here](https://github.com/dabit3/micro-frontend-example).

## Conclusion

Overall, setting up this project was fairly painless with the exception of all of the initial boilerplate setup.

I think in the future it would be nice to have some sort of CLI that handles much of the boilerplate and initial project setup.

If you have the need for this type of architecture, [Single-spa](https://single-spa.js.org/) definitely seems like the most mature way to do it as of today and was really nice to work with.

> My Name is [Nader Dabit](https://twitter.com/dabit3). I am a Developer Advocate at Amazon Web Services working with projects like [AWS AppSync](https://aws.amazon.com/appsync/) and [AWS Amplify](https://aws-amplify.github.io/). I specialize in cross-platform & cloud-enabled application development.