← Back to context

Comment by dfabulich

15 hours ago

https://2025.stateofhtml.com/en-US/features/web_components/

Sort by negative sentiment; Shadow DOM is at the top of the list, the most hated feature in Web Components. You can read the comments, too, and they're almost all negative, and 100% correct.

"Accessibility nightmare"

"always hard to comprehend, and it doesn't get easier with time"

"most components don't need it"

"The big issue is you need some better way to some better way to incorporate styling from outside the shadow dom"

> It's the only DOM primitive that allows for interoperable composition.

There is no DOM primitive that allows for interoperable composition with fine-grained reactivity. Your framework offers fine-grained reactivity (Virtual DOM for React/Preact, signals for Angular, runes for Svelte, etc.) and any component that contains another component has to coordinate with it.

As a result, you can only mix-and-match container components between frameworks with different reactivity workflows by giving up on fine-grained reactivity, blowing away the internals when you cross boundaries between frameworks. (And Shadow DOM makes it harder, not easier, to coordinate workflows between frameworks.)

Shadow DOM sucks at the only thing it's supposed to be for. Please, listen to the wisdom of the crowd here.

> There is no DOM primitive that allows for interoperable composition with fine-grained reactivity. Your framework offers fine-grained reactivity (Virtual DOM for React/Preact, signals for Angular, runes for Svelte, etc.) and any component that contains another component has to coordinate with it.

This just isn't true - composition and reactivity are completely orthogonal concerns.

Any reactivity system can manage DOM outside of the component, including nodes that are projected into slots. The component's internal DOM is managed by the component using whatever reactivity system it desires.

There are major applications built this way. They make have a React outer shell using vdom and Lit custom elements using lit-html for their shadow contents.

On top of those basics you can also have cross-shadow interoperable fine-grained reactivity with primitives like signals. You can pass signals around, down the tree, across subtrees, and have different reactivity systems use those signals to update the DOM.

  • You can do it, but that undermines the whole point of React: fine-grained reactivity.

    If the child component had been a React component instead, the children would have participated in the virtual DOM, and React could do a minimal DOM update when it was done.

    React can't even see the shadow contents of Lit elements, so React just has to update the light DOM and let Lit take over from there.

    (Same applies to Vue, Angular, Svelte, Solid, etc. Each framework has a reactivity system, and you have to integrate with it to get its benefits.)

    • You still get minimal DOM updates crossing shadow boundaries. This is true fact.

      React's vdom without shadow DOM passes props to components, which all return one big vdom tree, and then there's one big reconciliation. React used with shadow DOM evaluates smaller vdom trees per shadow root, and does more, but smaller reconciliations. It's the same O(n) work.

      But in reality it's often much _better_ with shadow roots because the common WC base classes like Lit's ReactiveElement all do change detection on a per-property basis. So you only regenerate vdom trees for components with changed props, and with slots that doesn't include children. So if children of a component change, but props don't, the component doesn't need to re-render. You can do something similar by hand with memo, but that doesn't handle children separately. The compiler will, of course, fix everything.

      Every other reactivity system works fine across shadow boundaries. Even the super-fine grained ones like Solid. The only issue with signals-based libraries like Solid is that they pass signals around instead of values, so to get true no-re-rendering behavior with web components you have to do that to, which means picking a signals library, which means less interoperability. The TC39 signals proposal points to a future where you can do that interoperably too.

I feel like it’s a niche feature that got way too much attention. In a past job, we maintained a widget customers could embed onto their page. How much trouble we had with parent styles creeping into our widget and ruining the layout! This would have been so much easier with shadow DOM effectively isolating it from the customer site; that is the only valid use case for it, I feel.

Yet, for actual web components, I entirely agree with you.