Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Svelte.js component property is undefined within script tag with customElement: true

This might possibly be how Svelte.js works, but I'm curious if I'm doing something wrong, or if there's a workaround.

If I set compiler option customElement: true, properties passed to components are not available in the <script> tag within that component. I use webpack with svelte-loader. Here's a simple example:

// index.html

<my-app foo="testing svelte"></my-app>
<script src="bundle.js"></script>


// script.js (bundled into bundle.js)

import App from './App.svelte';
customElements.define('my-app', App);


// App.svelte

<script>
    export let foo;
    console.log(foo); // undefined

    $: bar = foo.toUpperCase(); // Cannot read property 'toUpperCase' of undefined

    $: qux = String(foo).toUpperCase(); // No error, works
</script>

{ foo } // testing svelte - works, as expected

{ qux } // TESTING SVELTE - works, as expected

Also if customElement: true is not set, and the framework is instantiated with const app = new App(...) constructor, console.log(foo) would work, just as $: bar = foo.toUpperCase().

Could anyone explain why Svelte works this way? Thanks, cheers!

like image 287
galingong Avatar asked Jan 18 '26 05:01

galingong


1 Answers

With custom elements, props are not passed from the constructor, they are only set later on, on the component instance.

What I mean, is that with custom element, it's conceptually like doing this with a standard component:

const app = new App({ target })
app.$set({ foo: 'bar' })

Not that:

const app = new App({ target, props: { foo: 'bar' } })

That's why the value is initially (your console.log) undefined, but still shows as expected in the markup.

The reason for this is that the init function of a Svelte component (that is, the content of the <script> tag in your component) is called synchronously from the component constructor (that is, when you do new App(...)). However, at this point, attributes of the elements are not available (according to this answer).

Either give your prop a default value or check it before using it...

<script>
  // default value
  export let name = ''

  // ... or sanity check
  export let other
  $: otherName = other != null ? other + name : null
<script>
like image 165
rixo Avatar answered Jan 20 '26 18:01

rixo