Inorganik Produce

Sending events to a child component in Svelte 5

December 30, 2024

In Svelte 5, it's easy to dispatch events to parent components with a callback prop, and in Svelte 4 you can use createEventDispatcher() and dispatch an event. But what if you need to dispatch events in the opposite direction, from the parent component to the child? Admittedly the use-cases for this are rare. But I ran into such a case and wanted to share my solution.

The code

My solution was to make a super simple emitter class. An emitter instance is passed to the child as a prop where it can assign a prop function to the event property of the emitter.

ParentComponent.svelte:

<script>
// ...omitting some code here

class Emitter {
  setEvent(fn) {
    this.event = fn;
  }
}
const emitter = new Emitter();
</script>

<ChildComponent getValue={(val) => handleResult(val)} {emitter} />
<button type="button" onclick={() => emitter.event()}>Click me</button>

Whenever the parent calls emitter.event() the child component can do whatever it needs to in the event function, which gets assigned in an effect. It can call a callback prop function to send a value back up to the parent component:

ChildComponent.svelte:

<script>
	// ...omitting some code here

	let { emitter, getValue } = $props();

	$effect(() => {
		emitter.setEvent(() => {
      // do something here react to the event and/or get a value
      getValue(value)
    });
	});
</script>

Here's an example of this in the svelte playground.

Why?

Admittedly the use cases for this are rare, but there are legitimate ones. For getting values from a child component the simplest thing to do would be to use a $bindable() prop. But sometimes you need the child component to react to an event.

In my case I am using Threlte, the Svelte wrapper around Three.js. The way you compose components in Threlte is to have a container which holds the <Canvas /> element, and inside that you nest a scene component. I have a button in my canvas component to export an image of the canvas, but to get the image, you need to use the renderer, which is only accessible through a useThrelte hook in the scene. I couldn't put the button in the scene, so when clicking the button, I needed the child scene component to export the image via the renderer, how it looked at that exact moment.

I hope you enjoyed this snippet. Stay tuned for more fronted dev articles.

Links

My apps

Nomaste
Ghost AR