Using Scoped Slots in Vue.js
Multiple times I get to know companies that want to improve their productivity when coding in Vue.js.
That's a too open question, but at least some part can be achieved that by identifying what functionality they build pretty often in their apps, and then having a toolkit of reusable components that allow you to put that common logic in there, while being flexible enough to adapt to other apps.
Vue.js has slots in order to make components have a redefinable structure, but by themselves they're pretty limited. Sometimes you need some data or state in order to define how a component should render.
If you don't know slots, I suggest you learn them first on the Vue.js docs.
Scoped slots is an advanced feature in Vue.js that allows you to do that. They're like normal slots, but they can receive properties as well.
Let's build a Clock.vue
component in order to illustrate that. Basically it must be a time counter:
<template>
<div class="clock">
<slot :time="time">
<p>Default slot</p>
<p>Time: {{ time.toLocaleTimeString() }}</p>
</slot>
</div>
</template>
<script>
export default {
data: () => ({
time: new Date()
}),
created() {
setInterval(() => {
this.time = new Date(this.time.getTime() + 1000);
}, 1000);
}
};
</script>
You might have noticed the <slot :time="time">
line, that's the slot receiving the time
property. That's the way this Clock.vue
component can send the time data to the component that uses it, while encapsulating the timer logic itself.
You also might have realised that this component already renders the time by itself, as it has some default content within the slot.
But what if we want to redefine what it renders, while holding the timer logic in it?
Well, since we're passing the time
property to the slot, we can do that simply by using the v-slot
directive on the root element of the Clock
slot. So, wherever you want to render the Clock
component, you'll write something like:
<template>
<Clock>
<template v-slot="{ time }">
<p><b>Slot override!</b></p>
<p>Date: {{ time.toLocaleDateString() }}</p>
<p>Time: {{ time.toLocaleTimeString() }}</p>
</template>
</Clock>
</template>
v-slot
receives all the props passed from within the Clock
component. Since that's JavaScript object, we can use the Object Spread Operator to already grab the time prop as { time }
.
Do you see the power of Scoped Slots? You can do more powerful stuff, like render-less components, that you'll see in a future tip 😜.
If you want to run the code yourself, you can find it in this CodeSandbox!
Related Articles
The new Provide and Inject in Vue 3
Getting stuck into the prop drilling? Learn how provide/inject can make your components more flexible and independent in this short tutorial.
Jul 18, 2022
Sep 24, 2019
Create an ImageSelect component on top of vue-multiselect
Build an ImageSelect Vue.js component using the popular vue-multiselect package following the Adaptive Components pattern.
Feb 24, 2019
Sponsors
VueDose is proudly supported by its sponsors. If you enjoy it, consider supporting it to ensure the project maintainability.