Practical Svelte: The use-directive

Write better components by utilizing one of the lesser-known but extremely powerful features of Svelte: actions

Photo by NordWood Themes on Unsplash

Svelte is a great framework. It can feel like a breeze of fresh summer air. However, especially when starting out, it’s very easy to fall into the “this is how I did things in my previous framework” trap.

Happens to all of us, I guess. Definitely happened to me. And I don’t want it to happen to you. This is what this article is for.

Let’s say you want some of your input fields to automatically select all text on focus. Especially when coming from a framework like React, it might feel very tempting to start creating a component wrapper around the input (i.e. <input> becomes <Input/> ) and add your custom event handlers in there.

This is a great approach in React, but not so much in Svelte. Why, you ask?

Well, Svelte (at least in its current form, v3) really shines when you have native DOM elements at your disposal. You can use transition directives, conditionally switch CSS classes, bind to the current value with ease, and more.

And with a well-designed CSS styling system, you can go a very long way to consistently style the components the way you like while your code stays readable and maintainable.

But, alas, sometimes you want more. Sometimes, you may be tempted to write that wrapper. Because all your (React or Vue or insert your reactive framework here) instincts tell you so.

Resist the temptation. There is a better way. A svelter way. Introducing: the use-directive (a.k.a. “actions”).

Although they are part of the standard tutorial, I didn’t use them for quite some time. Part of the problem may be that the example from the tutorial felt rather specific and did not really seem to apply to the usual problem sets.

But once I understood their full potential, I absolutely came to love them.

Here’s how it works: You take any native component (like an input, a button, or a div) and assign it a function that is executed on the DOM node of the component when the component is mounted.

Let’s take our use case from above (select the text on focus). Assuming we have a basic input, like so:

<input type="text" class="ui-input" placeholder="Name" title="Name">

Step 1. We define a function that takes an HTML node (the node of the component on which the directive will be applied) and add our event listeners to the node.

selectTextOnFocus directive

Step 2. We assign this function to the component, using the “use-directive”.

<input type="text" class="ui-input" placeholder="Name" title="Name use:selectTextOnFocus>

That’s it. Yes, it’s that simple. Now your input will select all the contained text whenever it receives focus.

One important part — which is also explained in the standard tutorial — is to make sure you run the necessary cleanup code when the component is destroyed (i.e. unmounted from the DOM).

This is what the return value {destroy:…} is for. Otherwise, you might introduce a memory leak to your app.

You assign more than one directive to an element. So, we could create a new function called blurOnEscape and assign it as well.

<input type="text" class="ui-input" placeholder="Name" title="Name use:selectTextOnFocus use:blurOnEscape>

The code for this directive is again very straightforward:

blurOnEscape directive

Here are the examples from above in an interactive REPL.

The above examples are really basic, but the potential is huge. For example, at Faden, we wrote our multi-level drag and drop tree handling with a use directive, significantly slimming down the components itself.

Nevertheless, simple modular modifiers like the ones above often felt like the biggest productivity booster.

I would love to know your use cases. Also, let me know if you would like to see more advanced use cases, e.g. how to pass props to the directive and keep it in-sync with your component state.

And, since you read this until the end, here is one more example. This time in the interactive Svelte REPL: Listen for a click outside of an element and dispatch a custom event.

Very handy to use on floating elements of menus and dropdowns.