Custom elements offer many advantages, but they have a significant downside: versioning. Since custom elements are defined globally, if multiple micro frontends use different versions of the same custom element, the first to register takes precedence. This can lead to broken code if multiple versions of the same custom element are used.
To address this issue and avoid tight coupling between micro frontends, a versioning strategy can be used. This involves defining a suffix for each custom element, as shown in the following code example:
const componentName = 'hello-world';
const defineSuffix = (customElementSuffix) => {
const classes = {};
classes[customElementSuffix] = class extends HTMLElement {
constructor() {
super();
const shadowRoot = this.attachShadow({ mode: 'open' });
shadowRoot.appendChild(this.getTemplate());
}
getTemplate() {
const template = document.createElement('template');
template.innerHTML = `<div>Hello World</div>`
return template.content.cloneNode(true);
}
};
if (typeof customElementSuffix === 'undefined') {
throw new Error(`You must define a suffix for the ${componentName}.`);
}
const wcName = `${componentName}-${customElementSuffix}`;
if (customElements.get(wcName) === undefined) {
customElements.define(wcName, classes[customElementSuffix]);
}
return classes[customElementSuffix];
};
export { defineSuffix };
This code defines a function called defineSuffix
and exports it.
- The code begins by declaring a constant variable
componentName
and assigning it the value'hello-world'
. This variable represents the base name of the component. - The
defineSuffix
function is defined using arrow function syntax and takes a parameter calledcustomElementSuffix
. - Inside the
defineSuffix
function, a variableclasses
is declared as an empty object. This object will store the class definition for the custom element. - The
classes
object is populated with a new class definition. ThecustomElementSuffix
value is used as the key to define a new class that extends theHTMLElement
class. This class represents the custom element. - Within the custom element class, a constructor is defined. Then, it creates a shadow root using the
attachShadow
method with the mode set to'open'
. Finally, it appends the result of thegetTemplate
method to the shadow root. - The
getTemplate
method creates a new<template>
element, sets its HTML content to a string, and returns the cloned content of the template. - After defining the custom element class, the code checks if
customElementSuffix
is undefined. If it is, an error is thrown indicating that a suffix must be defined for the component. - Next, the code constructs the name for the custom element by appending the
customElementSuffix
to thecomponentName
. For example, ifcomponentName
is'hello-world'
andcustomElementSuffix
is'suffix'
, the resulting name would be'hello-world-suffix'
. - The code checks if a custom element with the generated name already exists using
customElements.get(wcName)
. This is to make sure we don’t get the custom element already used error messagef it does not exist (i.e., the result isundefined
), the custom element is registered usingcustomElements.define(wcName, classes[customElementSuffix])
. - Finally, the function returns the class definition for the custom element.
- The
defineSuffix
function is exported using theexport
keyword, allowing it to be imported and used in other modules.
Usage:
Each micro frontend would define a unique suffix for their custom element, like this:
import { defineSuffix } from './hello-world.js';
definedSuffix("suffix");
They would then use the custom element with the defined suffix, like this:
<hello-world-suffix></hello-world-suffix>
Summary:
In summary, this code provides a way to define and register custom elements with dynamic suffixes based on the provided suffix. This approach eliminates tight coupling between micro frontends and is particularly useful when a custom element is reused across multiple teams.