Lessons Learned Implementing a CMS Microfrontend
Chances are that you have already heard about “microservices.” The microservices architecture is a way of developing software applications by deploying many smaller, independent services working together. This approach gives you the liberty of picking the best-of-breed services and the flexibility of replacing them quickly.
At Contentstack, we have built our content management system based on this approach, and we are well aware of the many advantages that it brings to your app.
That’s why when we first heard about microfrontends — which is, as they say, extending the concept of microservices to your frontend — we were eager to implement it.
We tried adopting the microfrontends approach for one of our trial projects, and we learned some crucial lessons in the process. In this blog post, I will discuss some of the lessons we learned and other essential things to keep in mind while trying out microfrontends for the first time.
Why Did We Opt for Microfrontends?
A single unified frontend is sufficient as long as the app is not extensive. As the app grows and you add more features, the unified, large frontend starts impacting your delivery pace.
We faced a similar situation as our product is categorized into multiple subdomains. Furthermore, each of these subdomains is owned and developed by different teams. When we tried to combine the codebases of the various teams, the consequences were less than pleasing.
For starters, all the teams had to coordinate more than usual to make way for deployment and testing. And secondly, the multiple releases needed to be coordinated among the various team members.
Both of the above two reasons ensured concurrent merge conflicts.
Of course, we couldn’t continue operating on this model for long. Besides scaling, a bare necessity in IT was being compromised at the altar of a structure.
The only obvious way was to divide the frontend into small, manageable units.
There are multiple ways of building microfrontends. First, however, we had to find one that would be best suited to our end. Thankfully, we found two:
- The iframes method
- The one explained here
Initially, we opted for the iframes method. But unfortunately, it was not as effective as we wanted it to be. We faced issues related to SEO, security and usability; thus, we quit it. However, iframes helped us to create a primary cache and get started with the microfrontend development.
The next approach we took up was unique as we used custom elements. However, there were some challenges here as well.
Though the method could meet our requirements, we ran a risk of overkill since we did not work with multiple frameworks. FYI, we use ReactJS on most projects, and there are very few out-of-the-frame elements for those.
It seemed we were stuck in a never-ending loop until we came across a source that helped us with a few methods to implement the microfrontend the way we intended to use this method.
Once loaded, the container calls upon a method the window objects.
The microfrontend is rendered on the div provided by the container app. The illustration below shows how the setup looks.
The green area is rendered by the container app, and the blue section is controlled by the microfrontend.
Issues of Rendering Outside the Microfrontend
Indeed, we had some initial success, and it was thrilling. But unfortunately, it did not last for long.
We had yet another huddle only to realize that we needed to tweak the implementation process a bit.
The illustration shows that the microfrontend needed to render a few elements beyond the DOM hierarchy. The navigation bar on the left and the header needed to render some icons, buttons and other elements secured from the microfrontend.
After a few initial tryouts, we realized we could troubleshoot this using React Portal API.
This API allows one to render components beyond the DOM hierarchy where the microfrontend is rendered.
While we solved this challenge, we had yet another left to address. We required a seamless routing technique. However, controlling routes from the microfrontend was challenging. But creating a single-page application (SPA) with HTML 5 pushState API helped a lot here.
Using this, we passed down the history object from the container app to the micro-frontend app when initializing it. Additionally, we used relative routes, thereby resolving the issue.
More Challenges and Solutions
A quick solution was required.
We realized some libraries like React and Redux were shared among all the microfrontends; it made no sense to download these every time.
Instead, we decided to club these common libraries and download them all at once using a dII plugin from Webpack. This plugin helped us reduce our bundle size by 60%.
A few of the incubation projects currently have microfrontends based on this method. And, needless to say, all these applications are running seamlessly. As a result, it is now possible to roll out changes quickly without depending on other teams. Deployment and scaling have become more manageable.
Even though we have discussed our approach in-depth, you can refer to our microfrontend example on GitHub to know more about it.