Web Components have been around for a while, promising a standardized way to create reusable custom elements. It's clear that while Web Components have made significant strides, there are still several caveats that developers may face while working with them. This blog will explore some of these caveats.
a. Framework-Specific Issues
If you're deciding whether or not to use web components in your project. It's important to consider if web components is fully supported in your framework of choice or you may run into some unpleasant caveats.
1. Angular
For example to use web components in Angular it's required to add CUSTOM_ELEMENTS_SCHEMA to the module import.
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class MyModule {}
The problem with using CUSTOM_ELEMENTS_SCHEMA is that Angular will opt out of type checking and intellisense for custom elements in the templates. (see (see (see . Here's a couple of issues you may face while working with React v18. This is taken directly from the so you shouldn't need to use wrapper components anymore. However React v19 hasn't been officially released yet.
b. Micro frontends
The use of Web Components in micro frontends has revealed an interesting challenge:
1. Global Registry Issues
One significant problem is the global nature of the Custom Elements Registry:
Uncaught DOMException: Failed to execute 'define' on 'CustomElementRegistry':
the name "foo-bar" has already been used with this registry
This error occurs when trying to register a custom element with a name that's already been used. This error is common in micro frontends such as using module fedreration where multiple apps share a single index file. There is a proposal to address this called
Today utility class css frameworks like tailwindcss are very popular for a good reason. I'm not going to get into that in this blog. But unfortuently in web components at least with Lit we are limited to using CSS in JS. Not only is this less productive but we also need to make sure we setup our build system to handle minifying the CSS in JS template strings. If you don't do that it'll degrade the performance of your app because it increases the JS bundle size of your app. This is the major issue that CSS in JS frameworks ran into so they had to come up with zero runtime based solutions.
3. Event Retargeting
Normally without the shadow dom you may be used to events where target is a reference to the object onto which the event was dispatched. Whereas currentTarget is the element in which the event handler is attached.
However this works differently in the shadow dom. When a composed event is emitted within the shadow dom. The event will get retargeted so the target and currentTarget will be the Lit component that has the event listener.
Here's an example.
component-b
import {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
@customElement('component-b')
export class ComponentB extends LitElement {
override render() {
return html`<button>Click me</button>`;
}
}
When we click the <button> the button emits a composed event that bubbles.
component-a
import {html, css, LitElement} from 'lit';
import {customElement, property} from 'lit/decorators.js';
import './component-b.js';
@customElement('component-a')
export class ComponentA extends LitElement {
override connectedCallback() {
super.connectedCallback();
this.addEventListener('click', (e) => {
console.log(e);
});
}
override render() {
return html`<component-b></component-b>`;
}
}
Since the event is coming from component-b you might think that the target would be component-b or the button. However the event gets retarged so the target becomes component-a.
So if you need to know if an event came from the <button> or <component-b> you'll need to check the event's )
import {html, css, LitElement, nothing} from 'lit';
import {customElement, property, state} from 'lit/decorators.js';
@customElement('some-component')
export class SomeComponent extends LitElement {
@state()
private _isOpen = true;
render() {
return html`
<button @click=${this._toggle}>${this._isOpen ? 'Close' : 'Open'}</button>
${this._isOpen ? html`<slot></slot>` : nothing}
`;
}
private _toggle() {
this._isOpen = !this._isOpen;
}
}
<!DOCTYPE html>
<head>
<script type="module" src="./some-component.js"></script>
</head>
<body>
<some-component>
<input placeholder="Type here" >
</some-component>
</body>
Conclusion
Web Components offer a powerful way to create reusable and encapsulated custom elements. However, as we've explored, there are several caveats and challenges that developers may face when working with them. Issues with framework compatibility, limitations of the Shadow DOM, event retargeting, and the complexities of using slots are all areas that require careful consideration.
Despite these challenges, the benefits of Web Components, such as true encapsulation, portability, and framework independence, make them a valuable tool in modern web development. As the ecosystem continues to evolve, we can expect to see improvements and new solutions that address these caveats.
For developers considering Web Components, it's essential to weigh these pros and cons and stay informed about the latest advancements in the field. With the right approach and understanding, Web Components can be a powerful addition to your development toolkit.
SOCIAL SHARE CARD GENERATOR