Source code for this article is available on GitHub.
In this article
- In this article
- Introduction
- What is a Micro Frontend?
- Module Federation
- Why Vite?
- Creating a Micro Frontend using Vite + React
- Conclusion
Introduction
Micro Frontends: Enhancing Web Development with Vite and Module Federation
In this article, we'll explore the concept of Micro Frontends—a powerful architectural approach for web applications. Micro Frontends allow you to divide your front-end code into smaller, independently developed and deployable units. These units, known as micro frontends, offer numerous benefits, including increased development speed, scalability, and flexibility. By enabling different teams to work on separate parts of the front end while maintaining integration through an isolation layer, Micro Frontends help manage complexity and promote autonomy in front-end development.
What is a Micro Frontend?
A Micro Frontend is an architectural approach for web applications where the front-end code is divided into smaller, independently developed and deployable units called micro frontends. This approach enhances development speed, scalability, and flexibility by allowing different teams to work on separate parts of the front end while maintaining integration through an isolation layer. It's a way to manage complexity and promote autonomy in front-end development.
Module Federation
Module Federation is a key technology that enables a JavaScript application to load code dynamically from another application while sharing dependencies. When an application consuming a federated module lacks a required dependency, Webpack (the underlying technology) automatically fetches the missing dependency from the federated build source. This allows for efficient sharing and use of common libraries across multiple micro frontends.
Why Vite?
While Module Federation was initially introduced in Webpack, the landscape of JavaScript development has evolved. Vite has emerged as a game-changer by providing lightning-fast build times. Combining Vite and Module Federation can unlock immense capabilities for developing micro frontends quickly and efficiently.
Creating a Micro Frontend using Vite + React
Creating a micro frontend typically involves two main parts:
Host Application: This is the primary application that users interact with. It serves as the container for the micro frontends.
Remote Application: These are the micro frontends themselves, which act as building blocks for the host application.
Now that we have an understanding of the technologies we'll be using, let's dive into the practical implementation.
Setting Up the Host App
To create a host application using Vite and React, run the following command:
# Using npm 6.x
npm create vite@latest host-app --template react
# Using npm 7+, add an extra double-dash:
npm create vite@latest host-app -- --template react
Setting Up the Remote App
For the remote application, execute the following command:
npm create vite@latest todo-components -- --template react
This will create two folders, host-app
and todo-components
. Next, install the dependencies for both apps:
npm install
Creating Components in the Remote App
In the todo-components
app, create the following components:
// components/List.jsx
import React from "react";
const List = (props) => {
const { items } = props;
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
};
export default List;
// components/Input.jsx
import React from "react";
const Input = (props) => {
const { value, onChange, onSubmit } = props;
return (
<form
onSubmit={(e) => {
e.preventDefault();
onSubmit(e);
}}
>
<div className="flex-row">
<input
type="text"
value={value}
onChange={(e) => onChange(e.target.value)}
/>
<button type="submit">Add</button>
</div>
</form>
);
};
export default Input;
Now that the components are ready, make the following changes to the App.jsx
file:
// App.jsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";
import Input from "./components/Input";
import List from "./components/List";
function App() {
const [count, setCount] = useState(0);
return (
<>
<Input value={count} onChange={setCount} onSubmit={console.log} />
<List items={["Learn React", "Learn Vite", "Make an awesome app"]} />
</>
);
}
export default App;
Previewing the Remote App
To preview the components, run the following command in the todo-components
app:
npm run dev
You should see the following output.
Adding Module Federation to the Remote App
Now, let's add Module Federation to the todo-components
app. First, install the necessary dependencies:
npm install @originjs/vite-plugin-federation --save-dev
Next, configure Module Federation in the vite.config.js
file:
// vite.config.js in todo-components
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import federation from "@originjs/vite-plugin-federation";
export default defineConfig({
plugins: [
react(),
federation({
name: "todo-components",
filename: "remoteEntry.js",
exposes: {
"./List": "./src/components/List.jsx",
"./Input": "./src/components/Input.jsx",
},
shared: ["react"],
}),
],
build: {
modulePreload: false,
target: "esnext",
minify: false,
cssCodeSplit: false,
},
});
In this configuration:
-
name
: Specifies the name of the remote app. -
filename
: Sets the name of the file generated by Module Federation. -
exposes
: Lists the components to expose from the remote app. -
shared
: Declares shared dependencies, such as React, to optimize the bundle size.
Now, build the remote app:
npm run build
This generates a dist
folder in the todo-components
app containing a remoteEntry.js
file.
Serving the Remote App
Serve the remote app locally:
npm run preview
This serves the remote app on port 4173.
Adding Module Federation to the Host App
To use the remote app components in the host app, set up Module Federation in the vite.config.js
file of the host app:
// vite.config.js in host-app
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import federation from "@originjs/vite-plugin-federation";
export default defineConfig({
plugins: [
react(),
federation({
name: "host-app",
remotes: {
todo_components: "http://localhost:4173/assets/remoteEntry.js",
},
shared: ["react"],
}),
],
build: {
modulePreload: false,
target: "esnext",
minify: false,
cssCodeSplit: false,
},
});
In this configuration:
-
name
: Specifies the name of the host app. -
remotes
: Lists the remote apps to be used by the host app. In this case, we have one remote app, "todo_components," and we provide the URL of itsremoteEntry.js
file. -
shared
: Declares shared dependencies, such as React, to optimize the bundle size.
Using Remote Components in the Host App
Now, you can import and use the remote app components in the host app's App.jsx
:
// App.jsx in host-app
import { useState } from "react";
import List from "todo_components/List";
import Input from "todo_components/Input";
function App() {
const [newTodo, setNewTodo] = useState("");
const [todos, setTodos] = useState([]);
const onSubmit = () => {
setTodos((prev) => [...prev, newTodo]);
setNewTodo("");
};
return (
<>
<Input value={newTodo} onChange={setNewTodo} onSubmit={onSubmit} />
<List items={todos} />
</>
);
}
export default App;
Here, we import the components from the remote app using the specified remote name, "todo_components."
Serving the Host App
To serve the host app locally, run:
npm run dev
Now, you should see that the components from the remote app are being used seamlessly in the host app.
Conclusion
In this article, we've explored the concept of Micro Frontends and demonstrated how to create a micro frontend architecture using Vite and React, enhanced with Module Federation. By leveraging the power of Vite's fast build times and Module Federation's dynamic code loading capabilities, you can efficiently develop and scale web applications in a modular and maintainable way. This approach empowers multiple teams to work on different parts of the app independently, promoting flexibility and agility in front-end development.
For further exploration and to apply these concepts to your own projects, feel free to adapt and expand upon the examples provided here. Micro Frontends, when used judiciously, can significantly streamline your web development workflow.
Top comments (24)
Since Module Federation is a webpack thing, and since Vite does not use webpack, wouldn't the introduction of webpack negate the building speed of Vite? After all, I cannot possibly imagine that this works by compiling with Vite (rollup). I can only imagine that at least part of the building process is done by webpack.
Doesn't this mean that doing Vite with Module Federation is kind of a contradiction? Are we not mixing things that shouldn't be mixed? Aren't we losing rollup's build and going back to slower builds with webpack?
please check dependencies, there is no webpack involved
Isn't Module Federation a webpack feature? I'm confused if not.
it's a common misconception, module federation is just a principle to share code around on runtime.
Wow, this is an eye-opener. I'll read about it.
Module federation is a concept. Yes it was originally added to the webpack first but the original author was working on porting this to different build/pack libraries like vite.
You'll be able to find information under issues for each more vite/roll-up
I don't think this article is relevant anymore. Shared packages create multiple instances, other plugins fail to produce a digestible remote, etc.
Still doable but much more involved.
As I see it, vite.js is generally moving away from microfrontends and I can't blame them a single bit.
Module federation suddenly stopped working in Chrome(129) when I tried to run locally, But it was working with Chrome version 128, now i am testing locally on Firefox, any idea why latest release of chrome do not support it, when we run locally?
My vite.config.ts as below:
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import federation from "@originjs/vite-plugin-federation";
export default defineConfig({
plugins: [
react(),
federation({
name: "pokemonHome",
remotes: {
pokemon: "localhost:5174/assets/remoteEntry.js",
},
shared: ["react", "react-dom"],
}),
],
server: {
host: true
},
build: {
modulePreload: false,
target: "esnext",
minify: false,
cssCodeSplit: false,
},
});
Cors error in host-app, could you please help ?
Can you help me with the exact issue or any screenshots? I have uploaded the entire working on github so please do check that once and see if it's of any help.
CORS policy error is there with your github repo as well, enclosed screenshot, please check
localhost/:1 Access to script at 'localhost:3000/assets/remoteEntry.js' from origin 'localhost:5173' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
remoteEntry.js:1 Failed to load resource: net::ERR_FAILED
localhost/:1 Uncaught TypeError: Failed to fetch dynamically imported module: localhost:3000/assets/remoteEntry.js
Thanks for the update, I have edited the blog to reflect the changes and code is also updated on github.
The issue was how the remote app was being served, instead of
npx serve dist
, we can usenpm run preview
. This will serve the app on port4173
instead of3000
, so you would need to edit thevite.config.js
file inhost-app
to reflect this.These changes are updated on the blog post, let me know if that helps.
Happy Coding!! 🚀
For each of the remote Apps, are the "shared" libraries always loaded from the host app, or how does that work, which app provides the shared libraries?
Also any pointers on which things to implement when making a module federation setup like this production ready?
Thanks! great read btw. clear and simple to understand!
Yes, you are right. The "shared" libraries are always loaded from the host app. Few examples are
react
,react-dom
,react-router-dom
. These libraries need to be shared because if we have multiple variants in host and remote app, it will cause errors. If you find any libraries with this issue, you can include them in the "shared" libraries in host and remote apps.Happy coding!! 🚀
Great article, thank you for sharing but I have concerns when using with typescript. Does this import "import List from "todo_components/List";" work without warning or errors in IDE?
I am working on this now with typescript and the IDE does throw an error. I am sure there is a way to configure it to work, just need to figure out how....
So in my case, i just needed to add a
declarations.d.ts
file to the root of my src with:declare module 'yourRemotePath' {
const RemoteApp: any;
export default RemoteApp;
}
Great article, but i hate module federation with vite in react hot refresh doesn't support. Module federation with webpack you can see your changes in second your shell / container app.
This is a problem that is most likely resolved if your host app is itself a Vite + React project. However, I haven't tested much. It is the lack of "preamble" that kills HMR, and I think the preamble comes with a host name. So I guess, in a micro-frontend world, that one preamble per React MiFe is needed.
It is a complex matter. I once tried to get the attention of Vite about it, but I was shot down.
Good read as a beginner I was able to understand micro-frontends after reading this blog but I have encountered a bug on the inputs.You should check on that
Thank you for taking the time to read the blog post and for your feedback! I'm glad to hear that you found the post helpful in understanding micro-frontends.
I'm sorry to hear that you encountered a bug with the inputs. To better assist you and potentially address the issue, could you please provide more details about the bug you encountered? With more information, I'll do my best to assist you in resolving the issue.
It doesn’t render the newTodos in the UI rather it prints object.object on the input
Thanks for pointing that out, I have modified the article to fix the issue. Happy Coding 🚀
Some comments may only be visible to logged-in visitors. Sign in to view all comments.