avatarRaphael Duarte Sena

Summary

The article provides a comprehensive guide to building a remote micro-frontend application using Vite, React, and TypeScript, focusing on setting up the remote application and configuring it for multiple environments.

Abstract

In this guide, the author, a software engineer, shares their experience with building a micro-frontend architecture. The article emphasizes the use of Vite, React, and TypeScript to create a remote application that can be independently developed and deployed. It covers the initial project setup, strict port configuration for consistency, and optional Vite configuration for personal preference. The author also introduces the vite-plugin-federation to enable Module Federation, which allows for dynamic code sharing between micro-frontends. Practical steps include creating a sample feature component, exposing it through the federation plugin, and setting up build and serve scripts for different environments. The guide concludes with the author's encouragement for readers to follow along and explore the repository for further learning.

Opinions

  • The author believes that micro-frontend architectures help avoid repetition and centralize maintenance, making them a sweet solution for software development.
  • They express that there is a lack of content on micro-frontends, possibly due to the complexity of implementation, but that this complexity can be managed with the right approach.
  • The author suggests that organizing environment-specific files and using the @ alias for paths makes for cleaner and more scalable code.
  • They recommend disabling module preloading and targeting ESNext to ensure modern code practices in a micro-frontend setup.
  • The author values the use of Feature Flags for environment-specific configurations and testing, highlighting the importance of organized and switchable configurations for different environments.
  • They express enthusiasm and encouragement for readers to engage with the content, follow their profile for more insights, and contribute to the discussion by commenting or following the GitHub repository.

Build a Remote Micro Frontend with Vite, React, and TypeScript

Hello everyone, this is my very first article here at Medium about what I do for a living, which is being a Software Engineer and I'm very excited about it!

In this guide, I am using, Vite, Typescript, and React to build the project. Please check their documentation website.

Recently, I’ve been building a micro-frontend architecture that allows us to reuse components across different projects. This way, we avoid repetition and keep maintenance centralized. Sweet, right? 🤩

But I've faced some challenges where, there is not much content out there for this, since it’s something hard to implement I think companies just try to avoid the additional complexity or they don't know that we can avoid the “too much” complexity and have a smoother implementation and maintainable architecture.

So I will be showing and guiding you what I have found and created so far and I hope it helps you to achieve what you are looking for!

So you may be wondering…

ok, but what is a Micro-Frontend?

A very short description to keep our sake is that Micro Frontends is an architectural style that allows different teams to develop and deploy features independently, improving scalability and speeding up development. It’s like breaking a monolithic application into smaller, manageable pieces. For more details, check out this Micro Frontends overview.

Alright, in this article, we will focus on setting up the remote application for a micro frontend project using Vite, React, and TypeScript. The remote is like the cool kid that works perfectly on its own but eventually needs to play nice with the host. So, we’ll get this part up and running first. Then, next week, I’ll show you how to build the host and bring it all together. It’s gonna be fun — don’t miss it!

First, let's start by setting up our project with Vite and Typescript.

Setting Up Your Micro Frontend Remote Application

1. Create a Project Folder

mkdir micro-frontend-remote
cd micro-frontend-remote

2. Set Up Vite with React and TypeScript

Since we are already in the micro-frontend-remote folder, we can run the command below with the ., which means the project will be created in the same directory without creating a new folder for it.

npm create vite@latest .

When prompted:

  • Current directory is not empty. Please choose how to proceed: Choose Ignore files and continue.
  • Choose React as your framework.
  • Choose TypeScript + SWC as the variant.

3. Install dependencies

npm install

4. Run the project

npm run dev

You should see something like this.

Great! We just took the first step towards building our micro-frontend application. 👏👏👏

But first…

Change some default configurations.

1. Strict Port (Required)

For our micro-frontend work, we need to be consistent with where it’s hosted, so we cannot have random ports when we run the application, let’s change our package.json to look like this

{
  "name": "micro-frontend-remote",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite --port 5001 --strictPort --mode dev",
    "build": "tsc && vite build",
    "build:dev": "tsc && vite build --mode dev",
    "lint": "eslint .",
    "preview": "vite preview --port 5000 --strictPort",
    "serve:dev": "npm run build:dev && vite preview --port 5000 --strictPort"
  },
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@eslint/js": "^9.9.0",
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "eslint": "^9.9.0",
    "eslint-plugin-react-hooks": "^5.1.0-rc.0",
    "eslint-plugin-react-refresh": "^0.4.9",
    "globals": "^15.9.0",
    "typescript": "^5.5.3",
    "typescript-eslint": "^8.0.1",
    "vite": "^5.4.1"
  }
}

I added a --port 5001 --strictPort to my dev script so I can always run the development mode in the 5001 port as well as a serve:dev with port 5000 so this way I can serve and test it in the host application and can still run the development mode for changes if needed. I also created de :dev assuming that you may have different environments, so I will be adding the others as we go, if it is not your case, feel free to clean up and use only one.

2. Vite Configuration (Optional)

This configuration is totally personal so feel free to add them or not in your project, they just make my life easier and more organized. In your vite.config.ts file, do the following

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@/': new URL('./src/', import.meta.url).pathname,
    },
  },
  envDir: './src/environments',
});

Here I added the resolve and envDir, since I will be setting up this project for multiple environments, I like to organize all the respective files under a folder called environments in my src folder. And also I like to have the@ as my starting point since pretty much everything related to the project is inside src folder. But we need one more step for this to work fine since we are using Typescript, we need to also define the @ path there and say that it’s a valid path. So go to the tsconfig.json (it might be also called tsconfig.app.json)and add the path to it.

Another tip, for a cleaner view, I prefer to clean a bit of the file tsconfig.node.json, delete the tsconfig.app.json and replace the content of the tsconfig.json with this one below.

{
  "compilerOptions": {
    "paths": {
      "@/*": ["./src/*"]
    },
    "target": "ES2020",
    "useDefineForClassFields": true,
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "skipLibCheck": true,
    "moduleResolution": "node",
    "allowImportingTsExtensions": true,
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,
    "removeComments": true,
    "types": ["vite/client"]
  },
  "include": ["src"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

And your tsconfig.node.json should look like this

{
  "compilerOptions": {
    "composite": true,
    "skipLibCheck": true,
    "module": "ESNext",
    "moduleResolution": "bundler",
    "allowSyntheticDefaultImports": true,
    "strict": true
  },
  "include": ["vite.config.ts"]
}

Alright, so after these custom changes, your project folder structure should look like this. 👍👍

Installing plugin for Module Federation

Now that we have the project ready and configured, we are ready to jump into the world of Micro-Frontend with the Module Federation approach. There is a plugin that helps us to set up, it's the vite-plugin-federation that you can find the documentation here.

A short description of it is the vite-plugin-federation enables Module Federation in Vite projects, allowing multiple applications to dynamically share and load code from each other at runtime. It simplifies building micro-frontends by handling remote and host module loading more efficiently.

Ok, no need for more introduction, we see it’s good! so now let’s install it in our project.

Open the terminal and run

npm install @originjs/vite-plugin-federation --save-dev

Now we can start creating the components that will be exposed in our micro-frontend entry.

Creating a feature sample component

Under the folder src let's create a folder called features and inside this folder let's create a folder called feature-one and then our first component entry, create a FeatureOne.tsx file, which will hold the first feature of our micro-frontend.

Why the features folder? As I mentioned earlier, implementing a micro-frontend architecture comes with its challenges. One approach I found incredibly useful is creating a wrapper for each feature. By making the feature its own entry point and grouping all related components within it, it becomes much easier to manage and manipulate. This structure also makes it simple to create contexts if needed, promoting cleaner and more scalable code. We will see this in practice later on, no worries!

Ok, let's go back to the feature creation, inside the Feature.tsx you can add this

function FeatureOne(): JSX.Element {
  return <p>This is a Feature from a Micro-Frontend</p>;
}

export default FeatureOne;

Now, it’s time to make this feature available to our host, go to vite.config.ts and now we will use the vite-plugin-federation and let’s create a new federation entry point to it.

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";
import federation from "@originjs/vite-plugin-federation";

export default defineConfig({
  plugins: [
    react(),
    federation({
      name: "micro-frontend-remote",
      filename: "MicroFrontendRemoteEntry.js",
      exposes: {
        "./FeatureOne": "./src/features/feature-one/FeatureOne.tsx",
      },
      shared: ["react", "react-dom"],
    }),
  ],

  resolve: {
    alias: {
      "@/": new URL("./src/", import.meta.url).pathname,
    },
  },
  envDir: "./src/environments",
  build: {
    modulePreload: false,
    target: "esnext",
  },
});

Where federation creates the new entry point called microFrontendRemoteEntry.js and we are exposing the component that we just created, the FeatureOne.tsx . In thebuild configuration at the end I added two configuration settings, I strongly suggest you read about them in the plugin documentation but what I found is, that the most important ones are:

modulePreload: false because, in a micro-frontend setup, you may want to disable module preloading to prevent over-fetching of unnecessary chunks across different applications. By turning it off, you have finer control over what gets loaded, which can be important when using module federation where remotes might be loaded dynamically.

And target: ‘esnext’ that ensures your code remains modern, without unnecessary polyfills or down-level transformations. This is particularly useful in micro-frontend environments, as modern browsers often handle these micro-apps, and you avoid performance overhead by transpiling code for older browsers.

Great! So now we have the necessary configuration to expose our FeatureOne.tsx to our host.

Build Application and Serve the Remote Application

Make sure to always build the application before serving it, remember that when we serve the application, the browser will show what was just built, what you can do is combine the build and serve like we did in our package.json so you make sure that every time you serve you also build the application.

npm run serve:dev

You should be able to see the log in the terminal like:

If you go to http://localhost:5000 you should see:

But hold on! How do I see if the configuration we just created in the vite.config.ts file is working? 🤔

On the same window as your application is running, replace the URL with http://localhost:5000/assets/microFrontendRemoteEntry.js and voilà, the js file we just exposed in our vite configuration with our FeatureOne.tsx 🎉🎉.

And if you check your dist folder, you will see the file there.

Setup build and serve for different environments

Now we should be good to set up our multiple environments build and serve . Your package.json should look like this with this new setup.

{
  "name": "micro-frontend-remote",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite --port 5001 --strictPort --mode dev",
    "build": "tsc && vite build",
    "build:dev": "tsc && vite build --mode dev",
    "build:qa": "tsc && vite build --mode qa",
    "build:prod": "tsc && vite build --mode prod",
    "lint": "eslint .",
    "preview": "vite preview --port 5000 --strictPort",
    "serve:dev": "npm run build:dev && vite preview --port 5000 --strictPort",
    "serve:qa": "npm run build:qa && vite preview --port 5000 --strictPort",
    "serve:prod": "npm run build:prod && vite preview --port 5000 --strictPort"
  },
  "dependencies": {
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@eslint/js": "^9.9.0",
    "@originjs/vite-plugin-federation": "^1.3.6",
    "@types/react": "^18.3.3",
    "@types/react-dom": "^18.3.0",
    "@vitejs/plugin-react-swc": "^3.5.0",
    "eslint": "^9.9.0",
    "eslint-plugin-react-hooks": "^5.1.0-rc.0",
    "eslint-plugin-react-refresh": "^0.4.9",
    "globals": "^15.9.0",
    "typescript": "^5.5.3",
    "typescript-eslint": "^8.0.1",
    "vite": "^5.4.1"
  }
}

Where I added qa and prod for serve and build . Now you can have different values in different environments and test them according to your needs, especially when it comes down to testing the source of the micro-frontend, whether is in dev , qa or prod you will be always able to run the values locally and debug where is the problem you might be facing or how certain keys should look different in each environment.

For example, I work with the Feature Flag concept, which you can read more about here. So for each environment, we have a different client-key, to make sure they work fine I can add the value there and switch the configuration based on the environment.

The same works for the backend URL, you will have different backend URLs based on your environment, and you easily switch them with this configuration, and more than that, they are organized into one folder. 🙂

Conclusion Addition

Thank you for following along! 🙏

I hope this guide helps you set up your own micro-frontend remote application. If you have questions or want to share your experiences, feel free to comment below.

If you found this guide helpful, consider following my profile for more articles on micro-frontends and other exciting topics. Happy coding! 🎉

If you want to check the repository with this remote application, you can check it in my GitHub profile.

Next Step

React
Vite
Typescript
Micro Frontend
Programming
Recommended from ReadMedium