Skip to content

Not possible to set attributes (template syntax) on web components/custom elements #13104

Open
@bigwigal

Description

@bigwigal

Vue version

^3.0.0

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-dm4frnun?file=src%2FApp.vue,src%2Fmain.ts,src%2Fcomponents%2FAdditionComp.js&terminal=dev

Steps to reproduce

Inspect DOM and observe missing attributes on Vue/Lit instance, also observe incorrect calculation in render of Vue/Lit instance. Vanilla/Lit included for reference.

What is expected?

The component rendered within Vue to be identical to the vanilla/native DOM render.

What is actually happening?

Firstly, I can see that this isn't a bug from Vue's perspective, it's expected behaviour (documented here), but it's something that will undoubtedly cause plenty of bugs. I can also see that this has been reported as a bug before now (at least once here) but issues are now closed/old or appear to be indirectly related, so I'm raising this again to get a fresh perspective.

We're using Lit to create a component library and one of the requirements within the team is for these to be used within Vue. Stencil is mentioned in the linked issue above, which I haven't used, but like Stencil (at least at the time of writing) Lit also doesn't reflect by default. We've had our own discussions around this and are still not in agreement about what the best approach is. My personal opinion has been to only reflect if necessary, e.g. to expose state as hooks for CSS. This seems to be the approach taken in the native DOM where, other than in a handful of cases, markup represents initial state. This is also the view taken by Lit (documented here) and most likely what guided me on my own opinions, that and the fact that it just seems to makes sense to me. To reflect or not to reflect isn't really the issue here though, obviously it gets the attributes in the DOM but it doesn't address the potential issues caused by how values are actually set, i.e. value set via prop of same name (if exists) and everything just ends up defaulting to a string.

The problem with this approach is that it hijacks the setting of props from attributes, so any existing handling that may be in place is sidestepped. This is a particular problem when working with Lit and its pattern for the handling (conversion, validation etc.) of values being set from attribute to prop (documented here), although the same could be true of vanilla WCs and any handling that may be implemented within attributeChangedCallback. In that case, you could argue that handling could be moved to property accessors, which is a fair point although quite opinionated and not useful when using wrappers such as Lit (which at best would result in a load of boilerplate, manually requesting updates etc.).

System Info

System:
    OS: Windows 10 10.0.19045
    CPU: (8) x64 11th Gen Intel(R) Core(TM) i7-1165G7 @ 2.80GHz
    Memory: 16.03 GB / 31.69 GB
  Binaries:
    Node: 22.14.0 - ~\nvm-symlink\node.EXE
    npm: 10.9.2 - ~\nvm-symlink\npm.CMD
  Browsers:
    Edge: Chromium (131.0.2903.112)
    Internet Explorer: 11.0.19041.4355

Any additional comments?

I know that binding can be used to ensure the correct types (putting the onus on consumers) but any other handling would still be lost. I can also see custom directives have been suggested several times in related issues but I don't think that's practical in this case. We do have tooling for scaffolding projects (all Vite based) so an option to switch this behaviour off would be an acceptable solution, although still not ideal, as we can achieve that in a single place within config, and not on the markup of every custom element that is used.

I have huge respect for Vue (the project and people behind) but I know I will be put off using it due to this issue. I would expect that Vue would look to integrate more seamlessly with these tools, not put up barriers to using them, and as a minimum treat custom elements the same as native elements.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions