It seems you can't "map over" slots in a Web Components. E.g. if you have a <x-list> with one catch-all <slot> for children, you can't then wrap each child in a <li>. Everyone gives up and just pushes the structure up to the light DOM (<x-list><li>…</li></x-list>) or exposes some helper (<x-list><x-list-item/></x-list>. Amazing that *every time* I try Web Components they fail in the most basic ways, making it painfully obvious they were designed by people that don't actually do web development.
As usual with most new "Web API” features, the conventional wisdom that develops is “major portion of feature X is an anti-pattern”. In this itse, there seems to be a growing consensus that "shadow DOM" is an anti-pattern or should be used incredibly sparingly. Which is crazy since major features that WC were supposed to finally deliver (like style encapsulation) are *only* possible with shadow DOM. But its so difficult to use that people are now like "oh global styles are actually better".
If you dig into the actual complaints however, it quickly becomes obvious that people *do* want style encapsulation, just with certain very trivial features that Web Components do not provide. Half the requests are just around the very reasonable desire to control some amount of style boundary crossing (which the slot proposal thought it would handle, but doesn't). But since its so difficult to do these simple things, people just say “I guess style encapsulation is more trouble than its worth”.
Don't forget how long it took to get declarative shadow DOM, a feature that *any web dev* would have considered a v1 necessity since they'd otherwise *not be able to use it with their current server setups*. This is a problem that keeps repeating itself. Web API designers think have generally low opinions of web dev frameworks (e.g. React), and attempt to provide a "native" solution without understanding what is actually liked about the framework, and are then frustrated when devs don't use it.
It is still wild to me that Web Components are pitched as an alternative to React *at all* when the entire point of React was immediate mode rendering. That is literally the motivating feature for its creation. Web Components of course don't work this way. React would have still been invented if WC existed beforehand. Any literature that positions WC against React without *mentioning* this distinction is at best too uninformed to serve as an authority, and at worst willfully misleading devs.

@tolmasky

According to Safari devs, Apple wanted declarative web components, but lost to Google: https://x.com/rniwa_dev/status/1352322006448947203

On top of that it took the web components people until 2022 to finally acknowledge the problems (everyone had already told them since at least 2015) and to have any sort of roadmap https://w3c.github.io/webcomponents-cg/2022.html

@tolmasky

And yes, people in the web components space are either clueless, oblivious, or incompetent when it comes to web and web frameworks.

In 2020 Lea Verou writes about "the failed promise of web components" https://lea.verou.me/blog/2020/09/the-failed-promise-of-web-components/, then the Web Components CG is born to arrive at the report linked above.

Here's Rich Harris in 2019 calling out all the issues mentioned in the report: https://x.com/Rich_Harris/status/1198332398561353728

In 2024 Lea Verou keeps calling people like him haters: https://x.com/LeaVerou/status/1840134654852247765

The failed promise of Web Components • Lea Verou

@tolmasky You can, but you have to use manual slot assignment: https://knowler.dev/demos/hrLXPG1?codepen

Not super pretty, but in theory a pattern like this could be added to the platform.

“Wrapping slotted element with manual slot assignment” demo by Nathan Knowler

@knowler So if you do a replaceNode and swap out the second, is there some way to figure out the addedNode goes there, or do you have to like store a bunch of state yourself (like storing the index on each item, then looking for a "hole" when an item is added since I assume replace does a remove then add)?
Ugh apparently the mutation observer is async.
@tolmasky ya, you’d need to have your own system for mirroring the order of the light DOM items inside the shadow DOM.
@tolmasky I updated the example with some code to handle this. It just has a map of the slots by their slotted items. No need for indexes. We can look up the slot for adjacent light DOM items and then use that to figure out where the new list item + slot need to be inserted (i.e. if there’s no previous item, then we’re at the prepend it to the list; if there is a previous item, just insert with the `.after()` method).
@knowler Very nice, you should npm that, I think it would help a tonic people. You can go and respond to a bunch of old stack overflow questions too. Unfortunately I think the MutationObserver means it doesn’t work for what I’m doing since it is async (unless I am mistaken).

@tolmasky Good ideas. I think I’ll probably write something about manual slot assignment too because I think it’s fairly unexplored even by those who use custom elements.

For mutation observers, I know that for use in custom elements you need the element definition to occur before the body HTML is parsed otherwise it’ll miss the children and to avoid that you need to call something to find existing children when the element connects. iirc the `slotchange` event for regular slotting doesn’t suffer from this issue (i.e. it’s called when the shadow root is attached).