This content originally appeared on Bits and Pieces - Medium and was authored by Vishal Sharma
One of the main advantages of building micro frontends is the independent deployments and releases. With build time composition, we can publish the micro frontend independently but can not release the app without being dependent on the other micro frontends. This leads to a modular monolith.
To overcome this problem, it is advisable to compose micro frontends at run time so that:
- Any change in one micro frontend can be taken to production without worrying about other micro frontends aka Independent Deployments.
- Each team is free to consider the tech choices within their micro frontend aka Autonomous Teams.
- There is less coupling between the micro frontends and more cohesion within the micro frontend aka Modular Codebase.
There are multiple libraries like Webpack Module Federation, Single SPAs, and h-include available in the market to help the teams with run time micro frontends composition but then it is one more library to understand and manage instead of building the features.
Webpack modules federation has been the talk of the town since its launch as it has simplified micro frontend composition at run time. But with the emergence of faster bundlers like Vite, Turbopack, etc Webpack might not be the default choice for frontend applications anymore.
This article will discuss different patterns to compose micro frontends at run time without using any library or framework-specific technique.
1. Reverse Proxy Composition — Each application is an independent SPA
This composition technique is suitable when the whole page is a single micro frontend.
Taking the example of an e-commerce application, Catalog, Cart and User micro frontends can be deployed as three independently running applications on their web servers. But to make it work like a single seamless application, a proxy server is sitting in front of the end user to route it to the appropriate micro frontend according to the URL.
A simple node server working as a proxy may look something like this:
const catalogServer = "https://catalog.myapp.com",
cartServer = "https://cart.myapp.com",
userServer = "https://user.myapp.com";
function match(domain) {
return proxy(domain, {
proxyReqPathResolver(req) {
console.log(`${domain}${req.url}`);
return `${domain}${req.url}`;
}
});
}
server.use("/catalog", match(catalogServer));
server.use("/cart", match(cartServer));
server.use("/user", match(userServer));
server.listen(3000, (err) => {
if (err) throw err;
console.log(`> Ready on http://myapp.com`);
});
Pros / Use Cases
- Convenient technique for a project re-platforming to new technology. That also means each team can choose a different UI framework although not always recommended.
- Go to choice for Server Side rendering as each micro frontend can be an independent SSR application.
- Easy to manage as you only need an anchor tag to route to another micro frontend.
- There is no need for a container application, unlike build time composition.
Cons
- Only suitable when the whole page is a single micro frontend.
- There will be full page reloads to navigate from one micro frontend to another which can cause delays in the largest contentful paint. The routes within the same micro frontend can continue to work as a single-page app.
- The page layout might not remain consistent as each micro frontend need to handle the layout individually or by sharing a library.
- One more additional layer is needed for the reverse proxy server in the overall application architecture.
Please check this github repo for the complete example of reverse proxy composition.
2. Composition by lazy loading the micro frontends bundled using JavaScript
This composition uses the vanilla JavaScript way to dynamically add a new script tag to load the micro frontend.
Let's try to discuss the technique by applying it to two different scenarios
2a. When each page is a different micro frontend
In this case, we need a container application that would be responsible to route to different pages and then each page internally can render the micro frontend. The same container app can hold the layout for the overall app as well.
Using react-router, the container app routes for Catalog and Cart micro frontends would be:
<Routes>
<Route path='/products' element={<Catalog />} />
<Route path='/cart' element={<Cart />} />
</Routes>
and the catalog page component inside the container app can download and render the catalog micro frontend as:
export default function Catalog() {
const ref = useRef(null);
const renderMicrofrontend = () => {
window.mountCatalogMfe(ref.current);
}
useEffect(() => {
if (window && document && !document.getElementById('app-catalog')) {
const script = document.createElement('script');
script.id = 'app-catalog';
script.src = 'url for the catalog micro frontend';
script.onload = renderMicrofrontend;
document.head.appendChild(script);
} else {
renderMicrofrontend();
}
}, []);
return (
<div ref={ref} />
)
}
The IF condition checking document.getElementById('app-catalog') will make sure that the same micro frontend src is not getting downloaded when the user navigates back to the same page.
Inside the Catalog micro frontend, we have to make sure that we are exposing a method to render the micro frontend on the window object.
window.mountCatalogMfe = (el) => {
ReactDOM.render(
<CatalogApp />,
el
)
}
Now, we can use the same code to actually render the different micro frontends on different routes or even create a library for that.
Please check this github repo for the complete example of container based composition technique
2b. Multiple micro frontends in a single screen
Consider the example for a product details page where we can easily identify components from different domains rendered inside a single page.
Using the same technique we discussed above, we can render the Review as a micro frontend inside the product details page directly instead of a route change.
Pros / Use Cases
- A pure JavaScript-based framework solution to compose micro frontend at run time.
- No full-page reloading as the whole application behaves like a seamless single-page app.
- No need for an additional server to route to the appropriate micro frontend.
Cons
- In the case of each screen as a different micro frontend, we need a container application that would render the micro frontend on route change.
- We will end up downloading duplicate dependencies for each micro frontend but it can be resolved using something like webpack externals and then using the script tag to download those dependencies from cdn.
- Since each micro frontend should run independently also, we need to create two entry points in the configuration. One for the dev server to use and the other for the production build to expose a method on the window object.
entry: {
main: './src/index.js',
dev: './src/dev-root.js',
},
output: {
filename: '[name].js',
}
dev-root.js
ReactDOM.render(
<App />,
document.querySelector('#catalog-root')
);
index.js
window.mountCatalogMfe = (el) => {
ReactDOM.render(
<CatalogApp />,
el
)
}
Please check this github repo for the complete example of container based composition technique
It’s worth mentioning that one can also use iFrames or even web components to compose the micro frontends at run time as long as cross microfrontend communication, authentication, performance, acessibility etc. requirements are taken care of. So always make sure that you have understood the functional requirements before deciding on the composition pattern.
To understand which micro frontend communication technique can suit the composition patterns discussed above, check out the article on 5 different techniques for cross micro frontend communication.
💡 Note: Micro frontends are easier when you can apply a component-driven development approach to them. Bit makes this easier by providing an integrated dev environment (compiler, tester, linter, documentation, CI, dev server, and packaging/dependency management/bundler all-in-one) for building apps. Learn more here.
Build Micro frontends with reusable components
Bit’s open-source tool help 250,000+ devs to build apps with components.
Turn any UI, feature, or page into a reusable component — and share it across your applications. It’s easier to collaborate and build faster.
Split apps into components to make app development easier, and enjoy the best experience for the workflows you want:
→ Micro-Frontends
→ Design System
→ Code-Sharing and reuse
→ Monorepo
Learn more:
- Creating a Developer Website with Bit components
- How We Build Micro Frontends
- How we Build a Component Design System
- How to reuse React components across your projects
- 5 Ways to Build a React Monorepo
- How to Create a Composable React App with Bit
- How to Reuse and Share React Components in 2023: A Step-by-Step Guide
You Don't Need Another Library to Compose Micro Frontends at Run Time was originally published in Bits and Pieces on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Bits and Pieces - Medium and was authored by Vishal Sharma
Vishal Sharma | Sciencx (2023-04-11T04:53:00+00:00) You Don’t Need Another Library to Compose Micro Frontends at Run Time. Retrieved from https://www.scien.cx/2023/04/11/you-dont-need-another-library-to-compose-micro-frontends-at-run-time/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.