Skip to content

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

Closed
@trusktr

Description

@trusktr

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions