Vue.js Slots
Bijit Mondal
Posted on December 14, 2023
Slots
Vue.js implements the content distribution API using the element. Slots enables distributing content across other components. They're particularly helpful when creating reusable widgets. This approach ensures that distributed content is positioned precisely within the child component, as intended.
Slots content and outlet
We can create custom button component and use it.
Create a CustomButton.vue component, the template should look like this
<template>
<button class="custom-button">
<slot></slot>
</button>
</template>
<script>
export default {
name: 'CustomButton',
};
</script>
<style scoped>
.custom-button {
padding: 0.5rem 2rem;
border-radius: 77px;
background: #2f8d46;
box-shadow: 2px 4px 4px 0px rgba(#2f8d46, 0.15);
border: 0;
color: #fff;
font-family: Josefin Sans;
font-weight: 500;
}
</style>
Use the CustomButton Component inside another Component like below
<template>
<CustomButton>
Hello Btn
</CustomButton>
</template>
<script>
import CustomButton from './CustomButton.vue';
export default {
components: {
CustomButton,
}
}
</script>
Output
Using slots, the component renders the outer (with its custom styling) while the parent component provides the inner content.
Render Scope
The slot content has access to the data scope of the parent component because it is defined within the parent.
<template>
<CustomButton>
{{content}}
</CustomButton>
<span>{{content}}</span>
</template>
Both of the {{content}}
will render the same content.
Slot content doesn't have access to the child component's data
Fallback Content
Sometimes, it's helpful to specify a default message or content for a slot, which will be displayed if the parent component doesn't provide any specific content.
<!-- In CustomButton.vue -->
<button class="custom-button">
<slot>Go</slot>
</button>
After changing the CustomButton
if we use the component in parent component without any content, it will render the default content Go
<!-- In parent component -->
<CustomButton>Hello Btn</CustomButton>
<CustomButton /> <!-- Without any content for slot -->
Named Slots
Having one <slot>
element to inject content can satisfy some use cases. However, in other use cases where there is a need to utilize multiple elements. It is possible to achieve this with named slots.
Named slots are elements with a name attribute to allow for name spaced content injection, we can improve our CustomButton
to have a icon slot.
<template>
<button class="custom-button">
<slot name="context">Go</slot>
<template v-if="$slots.icon" class="slot-icon">
<slot name="icon"></slot>
</template>
</button>
</template>
<script>
export default {
name: 'CustomButton',
};
</script>
<style scoped>
.custom-button {
padding: 0.5rem 2rem;
border-radius: 77px;
background: #2f8d46;
box-shadow: 2px 4px 4px 0px rgba(#2f8d46, 0.15);
border: 0;
color: #fff;
font-family: Josefin Sans;
font-weight: 500;
}
.slot-icon {
display: flex;
}
</style>
In the above example the <slot>
elements has a special attribute, name. A <slot>
outlet without name implicitly has the name "default
".
To pass a named slot, Use a <template>
with v-slot
followed by the colon and slot name or use the shorthand of v-slot '#', so<template v-slot:context>
=> <template #context>
<div class="button">
<CustomButton>
<template #context> Hello Btn</template>
<template v-slot:icon>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="button__icon"
>
<path
d="M13.75 7h-3v5.296l1.943-2.048a.75.75 0 011.114 1.004l-3.25 3.5a.75.75 0 01-1.114 0l-3.25-3.5a.75.75 0 111.114-1.004l1.943 2.048V7h1.5V1.75a.75.75 0 00-1.5 0V7h-3A2.25 2.25 0 004 9.25v7.5A2.25 2.25 0 006.25 19h7.5A2.25 2.25 0 0016 16.75v-7.5A2.25 2.25 0 0013.75 7z"
/>
</svg>
</template>
</CustomButton>
<CustomButton />
</div>
Output
Dynamic Slot Names
v-slot also allows dynamic slot names, we can change in the previous button context a dynamic slot
<template>
<button class="custom-button">
<slot :name="context">Go</slot>
<template v-if="$slots.icon" class="slot-icon">
<slot name="icon"></slot>
</template>
</button>
</template>
<script>
export default {
name: 'CustomButton',
props: {
context: {
type: String,
default: 'context', // Default slot name
},
},
};
</script>
<style scoped>
.custom-button {
padding: 0.5rem 2rem;
border-radius: 77px;
background: #2f8d46;
box-shadow: 2px 4px 4px 0px rgba(#2f8d46, 0.15);
border: 0;
color: #fff;
font-family: Josefin Sans;
font-weight: 500;
}
.slot-icon {
display: flex;
}
</style>
<template>
<div class="hello">
<h1>{{ msg }}</h1>
</div>
<div class="button">
<CustomButton>
<template #[dynamicSlotName]> Hello Btn</template>
<template v-slot:icon>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 20 20"
fill="currentColor"
class="button__icon"
>
<path
d="M13.75 7h-3v5.296l1.943-2.048a.75.75 0 011.114 1.004l-3.25 3.5a.75.75 0 01-1.114 0l-3.25-3.5a.75.75 0 111.114-1.004l1.943 2.048V7h1.5V1.75a.75.75 0 00-1.5 0V7h-3A2.25 2.25 0 004 9.25v7.5A2.25 2.25 0 006.25 19h7.5A2.25 2.25 0 0016 16.75v-7.5A2.25 2.25 0 0013.75 7z"
/>
</svg>
</template>
</CustomButton>
<CustomButton />
</div>
</template>
<script>
import CustomButton from './CustomButton.vue';
export default {
components: {
CustomButton,
},
data() {
return {
dynamicSlotName: 'context',
};
},
name: 'HelloWorld',
props: {
msg: String,
},
};
</script>
<style>
.button {
display: flex;
align-items: center;
}
.button * {
margin-bottom: 10px;
}
.button__icon {
width: 2em;
height: 2em;
margin-right: 0.5em;
}
</style>
Scoped Slots
Scoped Slot provides local data from the component so that the parent can choose how to render it. Vue scoped slots allow to pass data from a child component to its parent component, and then access that data from the parent component's template.
Single Scoped Slot
- Send Data to Parent. Create a
ChildComponent.vue
<template>
<div>
<slot :platform="gfg" age="19" :year="2023" />
</div>
</template>
<script setup>
const gfg = "Bijit-Mondal";
</script>
To pass props to a single default slot, use the v-slot directive directly on the child component tag. The props will be available as the value of the v-slot directive, which can access using expressions inside the slot. Example below
- Receive Data from Scoped Slot. Inside
ParentComponent.vue
<template>
<div>
<ChildComponent v-slot="slotProps">
<p> {{ slotProps.platform }} at {{ slotProps.age }} on {{ slotProps.year }}</p>
</ChildComponent>
</div>
</template>
<script setup>
import ChildComponent from './ChildComponent.vue';
</script>
In the example above, slotProps is a name that you can choose yourself to represent the data object that you receive from the scoped slot. You can get the text string from the slot by using the platform property.
A scoped slot can also send static data, which is data that does not belong to the data property of the Vue instance. When sending static data, you do not need to use v-bind. Instead, you can use interpolation to render the text in an
tag.
Posted on December 14, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.