Skip to content

Commit ccbc101

Browse files
authored
Add web components essay (#3018)
1 parent df73ff2 commit ccbc101

File tree

2 files changed

+194
-0
lines changed

2 files changed

+194
-0
lines changed

www/content/essays/_index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ page_template = "essay.html"
4343
* [10 Tips For Building SSR/HDA applications](@/essays/10-tips-for-SSR-HDA-apps.md)
4444
* [Why I Tend Not To Use Content Negotiation](@/essays/why-tend-not-to-use-content-negotiation.md)
4545
* [Template Fragments](@/essays/template-fragments.md)
46+
* [Web Components Work Great with htmx](@/essays/webcomponents-work-great.md)
4647
* [View Transitions](@/essays/view-transitions.md)
4748
* [Model/View/Controller](@/essays/mvc.md)
4849
* [Is Rendering JSON More Efficient Than Rendering HTML?](https://github.com/1cg/html-json-speed-comparison)
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
+++
2+
title = "Web Components Work Great with htmx"
3+
date = 2024-11-13
4+
[taxonomies]
5+
author = ["Alexander Petros"]
6+
tag = ["posts"]
7+
+++
8+
9+
People interested in htmx often ask us about component libraries.
10+
React and other JavaScript frameworks have great ecosystems of pre-built components that can be imported into your project; htmx doesn't really have anything similar.
11+
12+
The first and most important thing to understand is that htmx doesn't preclude you from using *anything*.
13+
Because htmx-based websites are [often multi-page apps](https://unplannedobsolescence.com/blog/less-htmx-is-more/), each page is a blank canvas on which you can import as much or as little JavaScript as you like.
14+
If your app is largely hypermedia, but you want an interactive, React-based calendar for one page, just import it on that one page with a script tag.
15+
16+
We sometimes call this pattern "Islands of Interactivity"—it's referenced in our explainers [here](@/essays/10-tips-for-SSR-HDA-apps.md#tip-8-when-necessary-create-islands-of-interactivity), [here](@/essays/hypermedia-friendly-scripting.md#islands), and [here](@/essays/you-cant.md#myth-5-with-htmx-or-mpas-every-user-action-must-happen-on-the-server).
17+
Unlike JS frameworks, which are largely incompatible with each other, using islands with htmx won't lock you into any specific paradigm.
18+
19+
But there's a second way that you can re-use complex frontend functionality with htmx, and it's [Web Components](https://developer.mozilla.org/en-US/docs/Web/API/Web_components)!
20+
21+
## Practical Example
22+
23+
Let's say that you have a table that says what carnival rides everyone is signed up for:
24+
25+
<table>
26+
<tr>
27+
<th>Name
28+
<th>Carousel
29+
<th>Roller Coaster
30+
</tr>
31+
<tr>
32+
<td>Alex
33+
<td>Yes
34+
<td>No
35+
</tr>
36+
<tr>
37+
<td>Sophia
38+
<td>Yes
39+
<td>Yes
40+
</tr>
41+
</table>
42+
43+
Alex is willing to go on the carousel but not the roller coaster, because he is scared; Sophia is not scared of either.
44+
45+
I built this as a regular HTML table ([closing tags are omitted](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td#technical_summary) for clarity):
46+
47+
```html
48+
<table>
49+
<tr><th>Name <th>Carousel <th>Roller Coaster
50+
<tr><td>Alex <td>Yes <td>No
51+
<tr><td>Sophia <td>Yes <td>Yes
52+
</table>
53+
```
54+
55+
Now imagine we want to make those rows editable.
56+
This is a classic situation in which people reach for frameworks, but can we do it with hypermedia?
57+
Sure!
58+
Here's a naive idea:
59+
60+
```html
61+
<form hx-put=/carnival>
62+
<table>
63+
<tr>
64+
<th>Name
65+
<th>Carousel
66+
<th>Roller Coaster
67+
</tr>
68+
<tr>
69+
<td>Alex
70+
<td><select name="alex-carousel"> <option selected>Yes <option>No <option> Maybe</select>
71+
<td><select name="alex-roller"> <option>Yes <option selected>No <option> Maybe</select>
72+
</tr>
73+
<tr>
74+
<td>Sophia
75+
<td><select name="sophia-carousel"> <option selected>Yes <option>No <option> Maybe</select>
76+
<td><select name="sophia-roller"> <option selected>Yes <option>No <option> Maybe</select>
77+
</tr>
78+
</table>
79+
<button>Save</button>
80+
</form>
81+
```
82+
83+
<br>
84+
That will give us this table:
85+
86+
<table>
87+
<tr>
88+
<th>Name
89+
<th>Carousel
90+
<th>Roller Coaster
91+
</tr>
92+
<tr>
93+
<td>Alex
94+
<td><edit-cell name="alex-carousel" value="Yes"></edit-cell>
95+
<td><edit-cell name="alex-roller" value="No"></edit-cell>
96+
</tr>
97+
<tr>
98+
<td>Sophia
99+
<td><edit-cell name="sophia-carousel" value="Yes"></edit-cell>
100+
<td><edit-cell name="sophia-roller" value="No"></edit-cell>
101+
</tr>
102+
</table>
103+
<button>Save</button>
104+
105+
That's not too bad!
106+
The save button will submit all the data in the table, and the server will respond with a new table that reflects the updated state.
107+
We can also use CSS to make the `<select>`s fit our design language.
108+
But it's easy to see how this could start to get unwieldy—with more columns, more rows, and more options in each cell, sending all that information each time starts to get costly.
109+
110+
Let's remove all that redundancy with a web component!
111+
112+
```html
113+
<form hx-put=/carnival>
114+
<table>
115+
<tr>
116+
<th>Name
117+
<th>Carousel
118+
<th>Roller Coaster
119+
</tr>
120+
<tr>
121+
<td>Alex
122+
<td><edit-cell name="alex-carousel" value="Yes"></edit-cell>
123+
<td><edit-cell name="alex-roller" value="No"></edit-cell>
124+
</tr>
125+
<tr>
126+
<td>Sophia
127+
<td><edit-cell name="sophia-carousel" value="Yes"></edit-cell>
128+
<td><edit-cell name="sophia-roller" value="No"></edit-cell>
129+
</tr>
130+
</table>
131+
<button>Save</button>
132+
</form>
133+
```
134+
135+
We still have an entirely declarative [HATEOAS](https://htmx.org/essays/hateoas/) interface—both current state (the `value` attribute) and possible actions on that state (the `<form>` and `<edit-cell>` elements) are efficiently encoded in the hypertext—only now we've expressed the same ideas a lot more concisely.
136+
htmx can add or remove rows (or better yet, whole tables) with the `<edit-cell>` web component as if `<edit-cell>` were a built-in HTML element.
137+
138+
You've probably noticed that I didn't include the implementation details for `<edit-cell>` (although you can, [of course](@/essays/right-click-view-source.md), View Source this page to see them).
139+
That's because they don't matter!
140+
Whether the web component was written by you, or a teammate, or a library author, it can be used exactly like a built-in HTML element and htmx will handle it just fine.
141+
142+
## Don't Web Components have some problems?
143+
144+
A lot of the problems that JavaScript frameworks have supporting Web Components don't apply to htmx.
145+
146+
Web Components [have DOM-based lifecycles](https://dev.to/ryansolid/web-components-are-not-the-future-48bh), so they are difficult for JavaScript frameworks, which often manipulate elements outside of the DOM, to work with.
147+
Frameworks have to account for some [bizarre and arguably buggy](https://x.com/Rich_Harris/status/1841467510194843982) APIs that behave differently for native DOM elements than they do for custom ones.
148+
Here at htmx, we agree with with [SvelteJS creator Rich Harris](https://x.com/Rich_Harris/status/1839484645194277111): "web components are [not] useful primitives on which to build web frameworks."
149+
150+
The good news is that htmx [is not really a JavaScript web framework](@/essays/is-htmx-another-javascript-framework.md).
151+
The DOM-based lifecycles of custom elements work great in htmx, because everything in htmx has a DOM-based lifecycle—we get stuff from the server, and we add it to the DOM.
152+
The default htmx swap style is to just set [`.innerHTML`](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML), and that works great for the vast majority of users.
153+
154+
That's not to say that htmx doesn't have to accommodate weird Web Component edge cases.
155+
Our community member and resident WC expert [Katrina Scialdone](https://unmodernweb.com/) merged [Shadow DOM support for htmx 2.0](https://github.com/bigskysoftware/htmx/pull/2075), which lets you htmx process the implementation details of a Web Component,
156+
and supporting that is [occasionally](https://github.com/bigskysoftware/htmx/pull/2846) [frustrating](https://github.com/bigskysoftware/htmx/pull/2866).
157+
But being able to work with both the [Shadow DOM](@/examples/web-components.md) and the ["Light DOM"](https://meyerweb.com/eric/thoughts/2023/11/01/blinded-by-the-light-dom/) is a nice feature for htmx, and it carries a relatively minimal support burden because htmx just isn't doing all that much.
158+
159+
## Bringing Behavior Back to the HTML
160+
161+
A couple of years ago, W3C Contributor (and Web Component proponent, I think) Leah Verou wrote the following, in a blog post about ["The failed promise of Web Components"](https://lea.verou.me/blog/2020/09/the-failed-promise-of-web-components/):
162+
163+
> the main problem is that HTML is not treated with the appropriate respect in the design of these components. They are not designed as closely as possible to standard HTML elements, but expect JS to be written for them to do anything. HTML is simply treated as a shorthand, or worse, as merely a marker to indicate where the element goes in the DOM, with all parameters passed in via JS.
164+
165+
Leah is identifying an issue that, from the perspective of 2020, would have seemed impossible to solve: the cutting-edge web developers targeted by Web Components were not writing HTML, they were writing JSX, usually with React (or Vue, or what have you).
166+
The idea that [behavior belongs in the HTML](https://unplannedobsolescence.com/blog/behavior-belongs-in-html/) was, in the zeitgeist, considered [a violation of separation of concerns](https://htmx.org/essays/locality-of-behaviour/);
167+
disrespecting HTML was best practice.
168+
169+
The relatively recent success of htmx—itself now a participant in the zeitgeist—offers an alternative path: take HTML seriously again.
170+
If your website is one whose functionality can be primarily described with [large-grain hypermedia transfers](@/essays/when-to-use-hypermedia.md) (we believe most of them can), then the value of being able to express more complex patterns through hypermedia increases dramatically.
171+
As more developers use htmx (and mulit-page architectures generally) to structure their websites,
172+
perhaps the demand for Web Components will increase along with it.
173+
174+
Do Web Components "just work" everywhere? Maybe, maybe not. But they do work here.
175+
176+
<script>
177+
class EditCell extends HTMLElement {
178+
connectedCallback() {
179+
this.value = this.getAttribute("value")
180+
this.name = this.getAttribute("name")
181+
182+
this.innerHTML = `
183+
<select>
184+
<option ${this.value === 'Yes' ? 'selected' : ''}>Yes
185+
<option ${this.value === 'No' ? 'selected' : ''}>No
186+
<option ${this.value === 'Maybe' ? 'selected' : ''}>Maybe
187+
</select>
188+
`
189+
}
190+
}
191+
192+
customElements.define('edit-cell', EditCell)
193+
</script>

0 commit comments

Comments
 (0)