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
componentNameand assigning it the value'hello-world'. This variable represents the base name of the component. - The
defineSuffixfunction is defined using arrow function syntax and takes a parameter calledcustomElementSuffix. - Inside the
defineSuffixfunction, a variableclassesis declared as an empty object. This object will store the class definition for the custom element. - The
classesobject is populated with a new class definition. ThecustomElementSuffixvalue is used as the key to define a new class that extends theHTMLElementclass. This class represents the custom element. - Within the custom element class, a constructor is defined. Then, it creates a shadow root using the
attachShadowmethod with the mode set to'open'. Finally, it appends the result of thegetTemplatemethod to the shadow root. - The
getTemplatemethod 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
customElementSuffixis 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
customElementSuffixto thecomponentName. For example, ifcomponentNameis'hello-world'andcustomElementSuffixis'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
defineSuffixfunction is exported using theexportkeyword, 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.
