--
At Lab Digital we provide eCommerce solutions for a variety of clients. Some of these clients already have websites they wish to keep, just with added eCommerce functionality. For these clients we can provide a tailor-made set of front-end components reflecting their corporate identity which they can easily implement on their website in order to achieve full webshop status. These components include an add-to-cart button, a price indicator, a cart and a checkout flow. The client merely has to pass the relevant SKUs (product identifiers) to the add-to-cart and price components and we take care of the rest. The front-end components communicate with our commercetools implementation in order to provide all the required functionality.
In order to get these components to render on the client’s page, some technical hoops need to be jumped through. First, the elements you want to replace need to be detected. Then correct modules containing the React components need to be imported, the React elements need to be created and rendered inside the detected DOM elements. In order to have a truly effective application, you would also want to be able to pass some props to the React components as well as automatically detect new DOM elements which have been asynchronously added. In order to achieve all this, we at Lab Digital used to implement React Habitat. However, React Habitat has not been updated for quite a while and shows quite a few errors when using the dynamic DOM node detection, so we decided to write our own package. Some of the design choices made in React Habitat were clearly influenced by a Java style of code writing, which we believe not play well with Typescript. Because of these reasons we decided to write our own package, which you can check out as React Abode on npmjs.
Everything starts off with detecting which DOM elements we need to replace. The easiest way to do this is with a custom attribute which we can query for. For example, if you and I were making an eCommerce website and we wanted to render a shopping cart, we could do something like the code below. In this code we add an empty div with our component selector and we load our bundle which contains our react components and implements the micro front-end package.
By executing javascript shown below in our micro front-end package, we can find all elements with the data-component attribute. Then all we really need to do is load the proper React component and render them in these elements. Easy peasy!
The eagle eyed reader might have noticed that a module with a React component as a default export appeared out of nowhere. Before we render any components we need to make sure we have loaded our modules. We can export a register function from our package which does exactly that.
In the bundle where the React components are defined we could then register our React components.
And that’s it! That’s really all we need to do in order to load multiple react components dynamically on a HTML page. Of course, this is very basic. Let’s see if we can add some improvements in both speed and functionality.
Since it is 2020, being able to dynamically load more HTML and detect any elements we need to populate is pretty much a basic requirement. This is done easily enough by making use of the DOMNodeInserted event on the document body. Every time a new DOM node is added to the DOM our function which detects and populates targeted elements will be run.
The downside to this is that rendering React components is an asynchronous process during which new DOM nodes will be added. This means that every time a React element is rendered, our update function will be triggered quite a few times and the elements with our selector data-component will be populated over and over. We can prevent this by tagging all our elements before starting to render them and then filtering elements which have already been tagged.
Another piece of functionality we will definitely want is the ability to pass props to our React components. In this way, a user of our React bundle could do the following in their HTML to pass the SKU (product ID) to a price indicator component.
We can then grab these props in our javascript, clean them up a bit and pass them down to the React components! The React components can then use these props just as if they had come from any other parent component.
The method of passing props described above works very well if the components are rendered on a static HTML page. If the HTML is dynamic, for example because you are nesting your components in an existing React application, it would be nice to be able to detect prop changes and rerender, right? We can do exactly that by using the MutationObserver. The mutation observer is available on most browsers, even IE11! We can tell the mutation observer to observe specific HTML elements for changes. We can even tell it to specifically track changes in the attributes. When we set things up as shown below, each change in attributes for an observed element will cause a rerender with the newest props.
When using this method of rendering React, all components are rendered sequentially. This means that there will be a performance impact when rendering a lot of components on the same page. When measuring this effect with Lighthouse, we can see a performance score of 99 when loading 50 simple react components. When increasing the number of components to 1000, this performance score drops to 74. For most websites there would be no reason to use this many separate micro front-ends. If you were to really need that many components you could use an async renderer or wait for React concurrent.
As you’ve seen above, getting started with your own React micro front-end setup isn’t all that hard with some knowledge of vanilla JavaScript and HTML. If you enjoy this kind of thing, feel free to check out the code of React Abode or our current vacancies at Lab Digital.