Web components were on my rader as a native built-in feature that I was interested in using. But after using it, I have some thoughts on the technology.
y tho
Well… lets start with my…
Experience
Honestly, the experience with Web Components (atleast for me) wasn’t that bad. I was using it to add progressive enhancements / small interactivity in my site’s guestbook (hey btw check it out at /guestbook). But here’s the thing, I was using it all wrong. Or atleast not the way most people would think.
Here is what I did:
<guestbook-comments class="mt-10 gap-4 flex flex-col">
<!-- child slot replaced with data fetched -->
<slot />
</guestbook-comments>
<script>
import type { GuestBookEntry } from "../utils/guestbook";
class GuestbookComments extends HTMLElement {
public async addEntry(newEntry: GuestBookEntry) {
// ...
}
}
</script>
You can find the full source file on GitHub.
Web components are meant to be like Framework Components (in React for example), where a component itself does everything in a shadow-root. But here is the problem…
Web components don’t support SSR, and they also lead to worse SEO. Because they need to be defined with customElements.define()
which is only
available in the browser window
object. This means using shadow root as a namespace for creating elements will not render the elements if
JavaScript is disabled. They are client-only, and this is bad for me. I like having stuff working without JavaScript and this just looks like a useFootGun
2.0
What I was doing with web components could be just used as regular onsubmit
&& onclick
handlers. Yeah, I was kinda using them wrong. I was only doing
that to get stuff like:
<script>
class AddComment extends HTMLElement {
public async addComment() {
// types are funny
const guestbookComments: (Element & { addData(newEntry: GuestBookEntry): Promise<void> }) | null = document.querySelector("guestbook-comments");
// its the same method that I had defined before in the wc
if (guestbookComments) await guestbookComments.addEntry(stuff);
// ^^^^^^^
}
}
</script>
Although it was kind of unneccessary, I also liked this approach. Because instead of a JS-Only or HTML-Only component, where one doesn’t show anything without JavaScript and the other doesn’t update at all, this combines the best of both in a reusable component.
What if I actually used it in a real world project?
Will I ever use it in a real-world large app? heck no. But, if I had to, will I write vanilla? NEVER. I have seen the pain of writing low-level DOM, and therefore its just not worth writing something inferior to just writing a React / Svelte Component. If I ever need Web Components, I have Svelte! I can just compile it to web components and just use that instead of writing painful vanilla JavaScript. In fact, its so easy to compile Svelte -> Web Components, I will just show one in 2 lines:
<svelte:options customElement="web-component" />
<h1>Hello World</h1>
Do I think Web Components are good?
Yesn’t
I just think they can be better. The main issue I have with them is their API & SSR/SEO Limitations. I don’t get the trend of making native features ridiculously difficult to work with. Web Components don’t need to have such a hard API. Everytime a new feature comes out on the web, its either too low-level (like WCs) or too limited. We should have a delicate balance between the 2 tradeoffs. For small interactivity websites? Yes they are good, for larger websites? NO.
Conclusion
Although everyone likes to trash on Web Components, I think they are pretty OK. (Yea I have also kind of spread hate about it sometimes) They are a convenient feature for adding component-based interactive islands with vanilla JavaScript, but not something I would use all the time.