Depending on what other component libraries you’ve used, you may be used to handling events by passing callback functions to component properties, or using a special event syntax – Svelte supports both, though one is usually more appropriate than the other depending on your situation. This post explains both ways.

This is the basic button component that we will be using for this article.

<button on:click={() => alert('Life has never Svelte better')}>
	Hello World
</button>

The Property (prop) way

This method is widely known in the React community and its the idea of passing the action down as a prop to the component itself. With this setup, we can create a default action that should take place if one wasn’t passed down.

DefaultButton.svelte

<button on:click={buttonAction}>
  Hello World
</button>

<script>
  export let buttonAction = () => alert("Life has never Svelte better");
</script>

We can make use of this in another component by providing the action as a prop to the component.

AnotherComponent.svelte

<DefaultButton buttonAction={() => alert('I am going to Twitter')} />

<script>
  import DefaultButton from './DefaultButton.svelte';
</script>

Even if this component didn’t contain the buttonAction prop we would still get the default alert that was set up inside our DefaultButton component. One of the benefits of this setup is that we can have a default behaviour when the button is clicked.

The event dispatcher way

Svelte has its own built-in event dispatcher which allows you to fire custom events, these events aren’t the same as your DOM events, so they are not cancellable and they don’t automatically bubble up.

We have to import the method to create the event dispatcher from svelte, then we can dispatch a custom event.

DefaultButton.svelte

<button on:click={buttonAction}>
  Hello World
</button>

<script>
  import { createEventDispatcher } from "svelte";
  const dispatch = createEventDispatcher();

  export let buttonAction = () => dispatch("awesomeButtonEvent");
</script>

Notice that we don’t have a default behaviour if the button is clicked and an event wasn’t listened for.

AnotherComponent.svelte

<DefaultButton />

<script>
  import DefaultButton from "./DefaultButton.svelte";
</script>

The example above will give us no alert or anything because we aren’t listening to the custom event. However, if we do attach a listener to the button we can now fire our action when the button is clicked.

AnotherComponent.svelte

<DefaultButton on:awesomeButtonEvent={() => alert('I am going to Twitter')} />

<script>
  import DefaultButton from './DefaultButton.svelte';
</script>

Notice that we see the alert now. Also notice the syntax for the custom event – custom event listeners are handled with the on: directive. The event name is the first argument you pass to the dispatch function inside your component.

So you might ask what is the benefit of using the event dispatcher over just passing a prop down? In some scenarios, you will need to add an action to a button that is 3 or more components down and passing a prop all that way is considered prop drilling (it is frowned upon by some, meh each to their own). However in the case of using an event dispatcher, even though these events don’t bubble, we can easily pass them up using a shortcut that Svelte has.

AnotherComponent.svelte

<DefaultButton on:awesomeButtonEvent />

<script>
  import DefaultButton from "./DefaultButton.svelte";
</script>

NextComponent.svelte

<AnotherComponent on:awesomeButtonEvent />

<script>
  import AnotherComponent from "./AnotherComponent.svelte";
</script>

TopLevelComponent.svelte

<NextComponent on:awesomeButtonEvent={() => alert('I am at the top')} />

<script>
  import NextComponent from "./NextComponent.svelte";
</script>

All we are doing is telling the compiler that this event should be emitted to the next component as long as a function hasn’t been provided to invoke. And this way also fits more with data down, actions up.

Thanks to Josh Duff for reviewing this post and giving feedback to improve it. Feel free to get in-touch with me on Twitter @silentworks with questions.