Skip to content

Unable to set Custom Element property instead of attribute. #15455

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
trusktr opened this issue Mar 5, 2025 · 2 comments
Closed

Unable to set Custom Element property instead of attribute. #15455

trusktr opened this issue Mar 5, 2025 · 2 comments

Comments

@trusktr
Copy link

trusktr commented Mar 5, 2025

Describe the problem

In Svelte, this,

<script>
  import "my-lib/my-el" // import a native Custom Element definition (not made with Svelte)

  let foo = [1, 2, 3]
</script>

<my-el foo={foo} />

is resulting in the array getting stringified and passed in as a DOM attribute. It works (my custom elements handle array string values for example) but it is not good for performance.

A Svelte user that might be using my custom elements will see the custom elements work when they pass arrays as the type definitions allow, but it is not obvious that they are being converted into strings only to be converted back into arrays again.

The workaround is to escape out of the template using refs (bind:this) in order to set JS properties in JavaScript instead of in the template, which is not ideal for the developer experience (we want to stay in the template). The code becomes something like this:

<script>
  import "my-lib/my-el" // import a native Custom Element definition (not made with Svelte)

  let el
  let foo = $state([1, 2, 3])

  $effect(() => {
    el.foo = foo
  })
</script>

<my-el bind:this={el} />

Describe the proposed solution

In Vue we have <my-el foo.prop="..." foo.attr="...">. In Solid.js we have <my-el prop:foo={...} attr:foo={...}>.

In Vue, I can do the following and it will always set the prop instead of the attribute, so I can avoid the cost of string conversions:

<script setup>
  let foo = ref([1, 2, 3])
</script>

<template>
  <my-el foo.prop="foo" />
</template>

What about Svelte? There doesn't seem to be anything like this in Svelte unless the docs didn't mention it. I look here: https://svelte.dev/docs/svelte/basic-markup.

We need a way to explicitly choose to set a prop, or to set an attribute.

Solid.js also has <my-el bool:foo={value} /> which forces boolean attribute treatment based on the truthiness of the given value. Lit.dev also has explicit boolean syntax: <my-el ?foo=${value} /> to use truthiness of value.

React 18 was sending all values as string too, but they semi-fixed this in React 19 which will now detect a JS property and use the JS property instead of an attribute (but the control is still not explicit, so if React chooses a JS property, there's no way for the user to choose an attribute).

Related to being able to choose an attribute vs a property, users need the choice to avoid broken styles similar to what is described here:

So, regardless if an element has a prop, and attribute, or both, the user still needs choice for ultimate control of the DOM (i.e. without the framework getting in the way).

Here's a custom-elements-everywhere proposal that will add a new test to ensure that all frameworks allow control, which Svelte will currently not pass (unless Svelte's feature wasn't documented and I missed it):

Importance

would make my life easier

@Ocean-OS
Copy link
Contributor

Ocean-OS commented Mar 5, 2025

If the property name is in the element ('foo' in node) it should set the property instead of the attribute. Otherwise, it uses setAttribute.

@trusktr
Copy link
Author

trusktr commented Mar 6, 2025

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants