Understanding scoped slots in Vue.js

Cristi Jora

over 5 years ago

Understanding scoped slots in Vue.js

Vue.js 2 brought many nice features almost every major release. Many of them are used a lot especially by UI libraries. Perhaps, one of the most powerful features in Vue 2 are the Scoped Slots. So what does this strange set of words actually mean ? Why scoped ? Vue documentation says that:

A scoped slot is a special type of slot that functions as a reusable template
(that can be passed data to) instead of already-rendered-elements.

The sentence above kind of explains what scoped slots do, but it might be a bit too technical especially for beginners. To better understand what scoped slots are and why they are powerful, we will start by exploring slots first.

Slot example

To put it simply a slot can be seen as a:

  1. A placeholder for content.
  2. An empty space that you want to fill in with some content
  3. Some missing lines of code that you want to add later on easily.

Some usages for slots usually are:

  1. Generic components (Button, Modal, Card, Dropdown, Tabs etc)
  2. Layout components (App, Header, Navbar, Footer)
  3. Recursive components (Tree, Menu)

Let’s start with a button example

The example above has a button component Button.vue which can be easily customized to contain text, custom markup (in our case an icon) or even nothing. If we went the way of defining a text prop, then we would have limited ourselves to text only and we would’ve not been able to define a button with an icon so easily.

null

As stated above, the slot our Button component is simply an empty space that can be filled with whatever markup we want. For the 3rd button we filled it with <i class="fa fa-user"></i>Profile This is, perhaps one of the most powerful concepts in Vue.js. If you come from a react background, this is similar toprops.children

Now that we understand simple slots, we will move to scoped slots. Scoped slots are slot’s father or a slot on steroids. They allow even more customization and might be useful in various scenarios.

Scoped slot example

To put it simply again, scoped slots are basically slots that can provide contextual information from the component directly. A scoped slot can be also seen as a placeholder for content but which can pass some contextual information from the component

Some usages for scoped slots usually are:

  1. Generic lists
  2. Tables, grids
  3. Selects, autocomplete, components with exposed inner implementation details.

We will start with a generic list example:

Let’s break the example above into pieces. First, we created a List.vuecomponent which looks pretty much like this:

<template>
  <div>
    <slot v-for="item in items"
          :item="item">
      <!-- fallback content here -->
    </slot> 
  </div>
</template>

<script>
export default {
  props: {
    items:{
      type: Array,
      default: () => []
    }
  }
}
</script>
<style>
</style>

This is not very different from our previous Button example. There is a subtle difference which transforms a simple slot into a scoped slot in the example above. The difference is in the :item attribute which is passed to the<slot> tag.

Note that :item is simply a name chosen by us in this case. You can pass pretty much any and how many attributes you need or like. In our case the item contains all the necessary properties we need.

Now that we passed our item inside a v-for , we can use it when we declare our list component.

<template>
  <List :items="listItems">
    <div slot-scope="row"
         class="list-item1">
       {{row.item.text}}
    </div>
  </List>
</template>

<script>
import List from './components/List.vue'
export default {
 name: 'app',
 components: {
	List
 },
 data() {
	return {
		listItems: [
		 {text: 'First item', icon: 'fa fa-user'},
		 {text: 'Second item', icon: 'fa fa-copy'}, 
		 {text: 'Third item', icon: 'fa fa-cut'}
		 ]
	}
 }
}
</script>

The snippet above is the use of our List.vue component which simply shows some text. I’ve skipped the css part since it’s not very relevant for now.

You can notice that again there is a subtle difference in our implementation compared to our previous simple slot example. That difference is in theslot-scope attribute which basically links our attributes (item) passed to the<slot> tag. You can see that link in the image below:

null

The row naming for our scoped-slot is arbitrary and can be anything you like. If you use ES6, there is also the option of destructuring our row so we have a less verbose template slot-scope={item}

Advanced slots example

Now that we understand how scoped slots work, we will create a generic table that can be easily customized by using both slots and scoped slots

Our Table.vue implementation looks pretty much like this:

<template>
  <table class="table">
    <thead>
      <slot name="columns">
        <th v-for="column in columns">
          {{column}}
        </th>
      </slot>
    </thead>
    <tbody>
    <tr v-for="item in data">
      <slot :row="item">
        <td v-for="column in columns"
            v-if="hasValue(item, column)">
            {{itemValue(item, column)}}
        </td>
      </slot>
    </tr>
    </tbody>
  </table>
</template>
<script>
  export default {
    name: 'l-table',
    props: {
      columns: Array,
      data: Array
    },
    methods: {
      hasValue (item, column) {
        return item[column.toLowerCase()] !== 'undefined'
      },
      itemValue (item, column) {
        return item[column.toLowerCase()]
      }
    }
  }
</script>

It contains a simple slot for columns as well as a scoped slot for the main
content. It also has content inside our <slot> tags. This means that it will
use that content, unless we replace it with our content later on. Such an
implementation allows basic usage without boilerplate code as well as more
advanced usage with custom content.

null

When using our Table.vue component, we used both the columns slot and thedefault scoped slot for content. This granted us access to each item in the current table row via slot-scope="{row}" which makes it possible to customize each table row however we want, whether it’s text, image, buttons or any other valid markup/components.

Conclusion

Slots and scoped slots in Vue.js are one of the most powerful features and concepts. They let us achieve high component customizability with little code, code re-use while keeping our code clean and DRY. I can say for sure that pretty much all Vue.js UI libraries use slots and scoped slots and perhaps you have used them many times without even knowing :)

BinarCode is a software development company with focus on full stack web development and open source. We specialize in Javascript based technologies and aim to provide high quality, performant and maintainable solutions tailored to your needs http://binarcode.com

Comments

Explore more articles