Search code examples
javascripteventsevent-handlingsvelte

Dispatch event from child to grandparent in Svelte


The structure of my application is as follows:

  • Grandparent is a form
  • Parent is an input which calls an api
  • Child displays the results of the api call

In the child component I want to pass data up to the Grandparent, depending on which result the user clicks on. I tried doing this, using an Event Dispatcher. This unfortunately fails, as it seems that communication is only possible between parent and child. Is there a way to accomplish this?

This here is my (simplified) code:

// Grandparent
  <form on:submit|preventDefault={handleSubmit}>
    <Search bind:item={item} on:result={e => console.log(e)} />
  </form>

// Parent
   <div class="search-results">
      <Response data={res} />
   </div>

// Child
  <script>
    function returnData(data) {
      dispatch("result", data);
    }
  </script>

  <button on:click={() => returnData(data)}>Click</button>

Solution

  • In Svelte 3 and 4 you can bubble events by using on:<eventName> without any event handler afterwards:

    // Grandparent
      <form on:submit|preventDefault={handleSubmit}>
        <Search bind:item={item} on:result={e => console.log(e)} />
      </form>
    
    // Parent
       <div class="search-results">
          <!-- only this line changes in your code:
               bubble the result event -->
          <Response data={res} on:result />
       </div>
    
    // Child
      <script>
        function returnData(data) {
          dispatch("result", data);
        }
      </script>
    
      <button on:click={() => returnData(data)}>Click</button>
    

    In Svelte 5, use callback props instead, and pass them down (e.g. create a onresult prop for Parent and Child):

    // Grandparent
      <form onsubmit={handleSubmit}>
        <Search bind:item={item} onresult={e => console.log(e)} />
      </form>
    
    // Parent
       <script>
         let { item = $bindable(), onresult } = $props();
       </script>
    
       <div class="search-results">
          <Response data={res} {onresult} />
       </div>
    
    // Child
      <script>
        let { onresult } = $props();
      </script>
    
      <button onclick={() => onresult(data)}>Click</button>