Optimizing Micro Frontends with Stencil
As applications continue to grow in scale and complexity, organizations are continually implementing new architectural patterns to maintain a fast development pace. Many companies are seeing issues of scale in their frontend applications and they’re turning to micro frontends to combat this.
What are Micro Frontends?
Micro frontends are an architectural pattern that seeks to solve many of the problems associated with scaling large frontend applications quickly and easily. The concept of micro frontends is derived from the pattern of microservices. Microservices are separated aspects of an application’s backend that provide a singular service function. The microservice architecture has become very popular as it simplifies a monolithic backend by slicing it into its core parts. With more and more frontend applications growing in complexity, many are now adopting this architecture on the frontend.
Micro frontends are meant to split up an application into separate verticals, where each micro frontend encompasses a feature or page of the larger application. Each micro frontend consists of a UI as well as all of the business logic and data fetching necessary for it to operate on its own. It is, in essence, a mini application itself. By splitting the overall application into these mini applications, frontend development becomes much easier to manage at scale as these micro frontends can be dropped into any part of the application without needing to know the overall context.
An Example
Let’s imagine we have a large-scale application that has a chat feature. This chat feature allows a user to send messages to other users on the platform. It sits in the corner of most pages of the application, so as to always be readily available to the user. As a small, independent feature of our larger application, this chat feature is a perfect candidate for a micro frontend.
So let’s start building our chat micro frontend! Well, not so fast. Before we open up our editor and start typing, we need to decide what technology we want to use to build our micro frontend. Many may be quick to start building with their favorite frontend framework, but doing so may leave us with a fairly limiting chat feature. The micro frontend architecture becomes most useful when an application starts to grow in scale and complexity. In a large-scale environment, it is not uncommon for our application to use several different frameworks. In order for our architecture to be highly composable, our micro frontends need to support multiple frameworks. To make micro frontends that are as modular and reusable as possible, many developers are turning to web components to implement their micro frontends.
Web Components
Web components are a set of web platform APIs that allow us to create custom, reusable, and encapsulated HTML tags that we can use in our web applications, no matter the framework. As a framework agnostic implementation, web components are the best option for building interoperable micro frontends.
Let’s revisit our chat app. For the chat feature to support every frontend framework that may be used in the overall application, we would have to create a different version of the chat for each framework. This would result in a lot of duplicate work, and there would still be no guarantee that there would be parity between each version of the chat feature. A much more efficient and reliable strategy would be to implement the chat feature in a framework agnostic way. This is why web components have become such a popular choice for building micro frontends. You can write your micro frontend once as a web component and, because it’s based on web standards, package it to be used with any frontend framework.
On the surface web components seem great, but if you’ve ever written vanilla web components before, you may know that sometimes they become a bit cumbersome and difficult to develop as complexity scales. This is where Stencil comes in. Stencil allows us to develop our micro frontends with the familiar features of popular frontend frameworks, but still compile it as a web component so as to ensure interoperability. With Stencil, we can wrap our micro frontend web component to make it compatible with whatever framework consumers of our chat feature want to use. This way, the chat team only has to maintain one codebase and can easily provide their app to other teams, even as more and more frameworks are introduced.
Now let’s take a look at what our chat micro frontend may look like as a Stencil component.
NOTE: This is a pseudo code example to illustrate the general structure of a micro frontend
import { Component, Host, h, Prop, State } from '@stencil/core';
@Component({
tag: 'micro-chat',
styleUrl: 'micro-chat.css',
shadow: true,
})
export class MicroChat {
@Prop() recipient: string;
@Prop() sender: string;
@State() messages: string[] = [];
componentWillLoad() {
this.fetchMessages();
}
private async fetchMessages() {
// this method fetches all of the previous messages between the two users
// and add them to the "messages" array.
// it is called when the component is loaded
}
private async sendMessage() {
// this method is meant to send a message from one user to another
// and update the "messages" array with the new message.
// it is called whenever the user hits “send”
}
render() {
return (
<Host>
<h1>{this.recipient}</h1>
{this.messages.map(message => (
<message-blurb>{message}</message-blurb>
))}
<form onSubmit={this.sendMessage()}>
<message-box />
<input type="submit" value="Send" />
</form>
</Host>
);
}
}
While this example isn’t incredibly complex, it demonstrates the core aspects of a micro frontend. Our chat micro frontend is modular and autonomous, as it takes in a sender and recipient and then handles all of the business and data fetching logic itself. It is not dependent on any other part of the application. You’ll also notice that the chat app uses a message-blurb
to display previous messages and a message-box
to type out and format a message. These are subcomponents that may come from a company-wide design system, or perhaps they are developed and owned by the chat team itself. Either way, this shows that micro frontends are not just static UI components, but rather a package of interconnected subcomponents and services that perform a singular function.
Publishing and Consuming a Micro Frontend
Once we have our micro frontend built, we’ll need to incorporate it into our larger application. One of the advantages of creating micro frontends with Stencil is the ability to load the micro frontend into any application as a standalone instance. We can do this by publishing our web component micro frontend to a CDN, and then all of our consuming applications can include a script tag that links to that CDN. With this setup, we can continue to publish and version our micro frontend independent of the consuming applications. This makes it much easier to manage changes to our micro frontend, as everything can be controlled through the CDN.
In Conclusion
The micro frontend pattern is becoming increasingly popular as frontend applications continue to grow in complexity. This architecture offers teams a way to develop independently and work in parallel, promoting progress and efficiency in the process. Because they can be reused throughout an application, micro frontends drastically reduce the amount of duplicate development work needed. In order to maximize the reusability of a micro frontend, though, it should be written in a framework agnostic way. Stencil provides a familiar, feature-rich development experience, while also enabling developers to deliver their micro frontends as web components so they can be used with any frontend framework. All in all, Stencil strikes a great balance between micro frontend development experience and reusability.