Video Summary and Transcription
Microfrontends is an architecture used by big companies to split monolithic frontend applications into manageable parts. Maintaining a consistent look and feel across different microfrontends is a challenge. Sharing styles can be done through Vanilla CSS, CSS modules, or CSS in JS. JavaScript variables can be used in styles, but readability and runtime overhead are considerations. Sharing state in microfrontends can be achieved through custom events, broadcast channels, shared state managers, or custom PubSub implementations. Module federation with Vite allows for client composition and sharing dependencies. Configuration is similar to Webpack, and future work includes working on the QUIC framework.
1. Introduction to Microfrontends
Microfrontends is an architecture that allows us to split a monolithic frontend application into multiple microfrontends. We can have vertical splits, where each microfrontend is responsible for a specific page or multiple pages, or horizontal splits, where different teams manage different parts of the application. This architecture is used by many big companies to easily manage their frontend codebase. Even if you have a small application, you can use microfrontends to gradually replace parts of your legacy application. However, one challenge is maintaining a consistent look and feel across different microfrontends.
Hello, everybody! Welcome to this talk, Microfrontends with React and Module Federation with Vitz. I would like, with Microfrontends... I know that this is a React Advanced Conference, but I would like to share with you Microfrontends architecture for those that don't know really well into deep the architecture.
So, Microfrontends is more or less what is microservices, but for the front-end. If we have a monolith, we can split the server part in microservices to manage better the endpoints and also the features, but in this particular case, the front-end is a monolith. So we can slice the monolith, the front-end monolith into many microfrontends, and that's more or less the architecture. We can have multiple vertical domains, so we can have one frontend in React, one another in Angular, and for example, another in Vue. And theoretically, microfrontends is both, so the server part and also the frontend part. But how we can slice the monolith? So we can slice our application, frontend application, into different ways.
We have a vertical split, so one microfrontend for each page or multiple pages. The blue team is looking for the homepage, and the red team is managing the checkout. Or we can split in a horizontal way. The blue team is the owner of the menu and the app bar, and the red, the green and the red is managing the dashboards. So this is more or less how we can split the monolith in vertical and in horizontal. But do we need, do I need this type of architecture?
These are some of the companies that are using micro frontend, and as you can notice, these are big companies. So this architecture is born to divide the frontend application and manage easily the codebase into multiple teams. But if I have a mid or a small application, I can use this concept, this architecture for the Strangler partner. So if I have a legacy application, I can replace pieces of my application to renovate it. So if you have an house and you want to, for example, renovate your garden, you can rip off all the old garden and put a new one. So that's more or less the idea that we can apply to our legacy application. So there are a lot of challenges in this process. One of them is the look and feel. So here you can see it's a modern house. So if we go into this modern house, we visit a little bit the location. We can see the kitchen that is a modern kitchen. We can go to the living room. But if we go to the bathroom, and we go into the bedroom, and we see this different style, it's a little bit weird. So you can imagine this concept in our application. We go into the home page, and it's really cool. We go into the product page, and it's cool as well.
2. Managing Styles in Microfrontends
We need to share a style through the whole application to avoid different styles in different parts. We can use Vanilla CSS and inject style sheets, but we may face clashes. To avoid clashes, we can use npm package post CSS prefix wrap or post CSS prefixer for Vite. Another approach is to use CSS modules or CSS in JS, which allows locally scoped styles and easy management.
We go to the checkout, and we can see a completely different style. So our look and feel of the application need to be the same to avoid this kind of problematic or odd situation. So we need to share a style through the whole application, but how we can share these styles?
So we can use Vanilla CSS. So we have Microfrontend A and Microfrontend B. We can simply inject the style sheet inside of the page and we can define two classes, card title. So Microfrontend A has a card title with the color black and we have Microfrontend B with the same name with a color red. At the end, if we inject in this specific order the style sheets, we have the result is card title red, because the last CSS win override the other. So how we can avoid in this kind of situation this problem?
We can use npm package. This is for webpack, is post CSS prefix wrap. Basically, what is doing, is wrapping our CSS and put a prefix for all the CSS to avoid this clashes. It's quite used, more than 19,000 of download, weekly download in npm. But if your application is in Vite, you can use post CSS prefixer. As you can see here, we are defining the selector, and the output is prefix underscore underscore selector. So, it's wrapping for us in the building process, it's wrapping the CSS and giving us a possibility to define a CSS without clash other CSS from other micro frontends.
We have an independent possibility to define our class and use them. Or we can use another way is to use CSS modules. So, we can define this kind of type of file, so card.module.css, we define our CSS, so card background color black. Then we can import this module.css into our React component. And we can use this object styles.card. So, this card is basically our CSS, we can inject into the JSX of our React component. And the build process output will be more or less like this. So, card this is the name of the module. Card is the main name of the CSS. And we have this unique ID that avoids clashes through the other CSS. And this is locally scoped. So, we don't have a problem with other cart properties, cart CSS into the other files.
Another way to manage the style is CSS in JS. It's locally scoped style, like the CSS model we saw before, so we avoid clashes. It's a good approach for the co-location because we can co-locate the CSS into our JavaScript file or TypeScript file, so into our React component. The CSS is really nearby our component, so it's easy to manage the style and the other things.
3. Using JavaScript Variables in Styles
You can use JavaScript variables in styles, making it convenient to use specific variables for padding or other purposes. However, there are some costs associated with this approach. The core code readability becomes more challenging, especially when using CSS modules, as the specific hash used in CSS modules requires inspecting the DevTool to understand the CSS better. Additionally, CSS in JS adds runtime overhead to the application.
You can use JavaScript variables into styles, so if you have a specific variable for padding or whatever, you can use this variable into the CSS and it's really handy. There are some costs. The core code readability, like the CSS module, in the DevTool of Chrome, or your browser, it's a little bit difficult, because if you remember in the CSS module, we have this specific hash, so we need to look into the DevTool to understand better our CSS. And this specific CSS in JS adds runtime override. So when our application is running, it's adding a little bit of a read for our application.
4. Sharing State in Microfrontends
To share the state in microfrontends, we can use custom events, broadcast channels, or a shared state manager like ZooStand. However, it's important to share as little information as possible to avoid coupling the microfrontends. Another option is to create a custom PubSub implementation, but it may introduce dependencies between microfrontends and rely on the window object.
Another big problem to solve in this kind of architecture is to share the state. So we have multiple ways to share the state. So we can use a custom event. So one microphone tends to call custom events, and another one is listen to it. This is a really basic implementation, and we are using Vanilla API of the browser. So it's really good. We can use broadcast channel. And this approach allows us to use Win, pass the information through Windows, tabs, iframes. So if we have some iframes, it's a really nice approach.
We can have a share state manager. So for example, we can have a ZooStand state manager. And we can share the instance of this ZooStand through our microphone tense. Keep in mind that in your architecture, you need to share as less as possible information, because you can share like the theme or other things. But don't share too much because the basic approach for micro frontends is to the couple, the deploy and also the development. So if you share too much with other micro frontends, of course, you are coupling the micro frontends, and it's difficult to end up with this problem.
So we have the last possibility so we can have custom PubSub implementation, so we can create our own implementation for managing the state through our micro frontends. So here is a small snippet. I grabbed this small snippet from this article from my friend Florian Rappel. So thanks. I would like to share and look at this snippet with you. So this is the code from the shell, the app shell that is loading all the micro frontends. So we are defining in the window a publish function, a subscribe function and unsubscribe function and then we have 11 listeners for PubSub. In the micro frontend A we can basically subscribe to this topic micro frontend A, and in the micro frontend B we can publish something to this specific topic. So if we call window.publish, we are dispatching an event with detail and topic and message. With this particular trick of a variable we are calling at the end the topic handlers because the window.subscribe() here is saving all the message function. So when message arrives, someone publish something into a topic, into the topic handler we are looping to all these topic handlers and we are calling the function. So finally at the end of this process, microfrontend A is able to get their information. I don't like too much this implementation, it's a simple implementation and you can start from it, that's why I decided to put this a snippet into the slides. I don't like because microfrontend B need to know, let me say, that there is another microfrontend and as we mentioned before, we would like to decouple as much as possible our process. And another thing that I don't like too much is the window object, so we are using the window as a shared object through all our architecture.
5. Client Composition with Module Federation and Vite
In this talk, we'll explore client composition using module federation with Vite. Module federation allows us to share dependencies and create a more modular architecture. With the release of the open-source module federation model by Zach Jackson, we can now use module federation with Vite. I created a library for module federation with Vite, which can be used with various frameworks. The implementation of Vite module federation is similar to the Webpack one, allowing for mix and match usage. To use module federation with Vite, you need to define a special configuration and await the federation function.
But as I mentioned before, for starting a minimum approach and then we can add the feature and another feature, it's really cool.
Okay, so we can composite this architecture in many ways. We can composite the architecture into the server side, so the server is grabbing all the information and it's creating our application for us because at the end, the application is always one, so the end user go through one application. And we can have also the edge, edge composition, so to the Lambda or CDN, we can composite all the micro-frontends and we can have client composition.
So in this talk, I would like to go into the client composition and for client composition, we have a module federation. So we have a Webpack. And in August 2022, Zach Jackson, the I think the main guy that is looking for module federation, release the open source, this specific model. So I work in pair programming with my friend, Manfred Steyer. And with this particular possibility that Zach Jackson gave us to the open source, the library, we create the possibility to do a module federation with Vite. So the other main bundler out there. So Manfred created this native federation layer. So it's a layer of a native API, and create also the angular implementation on top of this layer. I created a more general purpose module federation Vite. And you can use this library for Svelte, React, Vue or whatever. And so now module federation, we can use module federation with Vite as well. So this is really, really awesome. I created this library in my GitHub profile. And Jack Jackson saw my work and decided to move all the code into the module federation organization. So if you go into the module Federation slash Vite, you will find this plugin. You can find also the implementation for React and many other framework. So I archive my project and I move all the code there. So how we can share the dependencies. So another big thing into the architecture is to share the dependencies. And fortunately, the implementation of Vite module Federation is really similar to the Webpack one. So the original one. So you can mix and match module Federation with Vite and module Federation with Webpack. And you need to define for Vite, you need to define a configuration, a special configuration so you can await the federation function. Because a plug-in in Vite are more or less a function. You can grab this function from module Federation slash Vite. You can and you need to define configuration and few other options.
6. Configuration and Future Work
This configuration is similar to the Webpack one. You can define a name, expose the component to share with the main application, and share libraries. By sharing libraries, you can avoid downloading the same code base multiple times and reuse the cached version. In this case, we keep react-dom/server and server.node. I'm a FoodStack developer for clarinet, an open source developer in my free time. I'm currently working on QUIC framework and believe it will be a great framework for the future.
And what is this configuration? This configuration is really, really similar to the Webpack one. So you can, this is a configuration for a remote. that is loaded from our shell application. So you can define a name, you can expose the component that you would like to share with the main application. So this is your micro front end basically. And you can share also the libraries. So here we are sharing all our libraries, but you can define one by one a specific library. So if two micro front ends need the same library and the same version, we can avoid to download twice the same code base, but we can reuse the only one that we are in cache. And basically we can keep some other library that we don't want. So in this specific case, we keep react-dom slash server and server.node.
Okay. That's all. I'm a FoodStack developer for clarinet. You can find me on X, and my handle is at georgio underscore boa. Or you can find me on LinkedIn with my name and surname. I'm an open source developer in my free time. And currently I'm working hard on QUIC framework. Since 2022 I work with QUIC. I'm core maintainer collaborator. I'm pushing in the core code base as well. So if you would like or if you are interested in looking to QUIC, please have a look because I think it will be great framework for the future. Thank you for listening and have a nice conference.
Comments