Vue.js Slots: Why would you need them?

April 13, 2016, 5:30 pm

You've probably read the Vue.js documentation on slots. For me, that feature went from "why would you possibly need that" to "how could I have possibly worked without it" real quick.

While the documentation explains the concept, here's a real world example on how slots can improve your application codebase.

In my opinion, slots are one of the most useful and interesting features of vue. While this is a proposed standard for web components, I am not aware of other frameworks which have implemented this. It's easy to explain what slots do, but it's hard to explain exactly why they are useful (without using the term 'composability'), so I will give you a use case.

Let's say your app has a lot of forms. And your form has a few "sections":

  • Form heading: containing info on what the form is
  • Form elements: For the users to enter
  • Input buttons

Now you are trying to abstract the form into a separate component. Your colleagues who use react and angular tell you to use something like React's Children or Angular 1's Transclude and you find your way to the slots documentation.

You build a component like this:

<!-- FormHelper.vue -->
<form class="form">
  <h3>{{ title }}</h3>
  <slot></slot><!-- children will go here -->
  <button>Submit</button>
</form>

And you use the helper like this:

<!-- App.vue -->
<form-helper title="Password Reset">
  <input type="text" name="name" value="">
  <input type="text" name="email" value="">
</form-helper>

Now this works fine. Your manager tells you that a specific form needs a block of text after to the title for help message. And the button name should be "Request". This is outside the scope of the "children".

<!-- FormHelper.vue -->
<form class="form">
  <h3>{{ title }}</h3>
  <div :v-if="hasHelp">{{ helpMessage }}</div>
  <slot></slot><!-- children will go here -->
  <button>{{ customButtonName }}</button>
</form>

Now there might be additional problems coming up: multiple buttons, more interactivity, etc. Thankfully, in the vuejs world, there's a much cleaner solution: named slots. You modify the form helper like this:

<!-- FormHelper.vue -->
<form class="form">
  <h3>{{ title }}</h3>
  <slot name="help"></slot>
  <slot name="elements"></slot><!-- children will go here -->
  <slot name="buttons"></slot>
</form>

And here's how you use the same form:

<!-- App.vue -->
<form-helper title="Request Form">
  <div slot="help">
    Please ask your manager before requesting hardware.
  </div>
  <div slot="elements">
    <input type="text" name="item_name" value="">
    <input type="text" name="item_value" value="">
  </div>
  <div slot="buttons">
    <button type="button" name="button">Request</button>
  </div>
</form-helper>

The key idea for me is this:

Think of slots as passing components as props to a child component. Like how we pass strings, integers and objects, you're passing an entire sub-dom tree which the child can use in any place that it needs.

A few other places you can use slots:

  • If you're building stuff on material design. For example, the "Cards" material design system.
  • Modal windows and dialogs in general
  • Things like Bootstrap Panels and Custom Content

A few more amazing things about using slots:

  • A child component can be used for styling/presentation and the business logic can be kept in the parent element.
  • If you don't pass anything to a slot, nothing is shown. This lets you re-use them quite nicely and only pass in the slots that you want.

Photo Credit: Eric Kilby (cc-by-sa)

Next Post Previous Post