Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.
In this article, we’ll look at named and scoped slots.
Named Slots
Sometimes we want to have multiple slots. To distinguish them, then we have to name them.
We can define a component with named slots with the name
attribute as follows:
Vue.component("layout", {
template: `
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
});
Then we can use the component above together as follows:
src/index.js
:
Vue.component("layout", {
template: `
<div>
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
`
});
new Vue({
el: "#app"
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<layout>
<template v-slot:header>
<h1>Header</h1>
</template> <p>Main</p> <template v-slot:footer>
<p>Footer</p>
</template>
</layout>
</div>
<script src="src/index.js"></script>
</body>
</html>
The slot with the name header
is filled with:
<template v-slot:header>
<h1>Header</h1>
</template>
The slot with the name footer
is filled with:
<template v-slot:footer>
<p>Footer</p>
</template>
The slot with no name is filled with:
<p>Main</p>
If we want to wrap the content for the slot with no name with v-slot:default
as follows:
<template v-slot:default>
<p>Main</p>
</template>
Either way, the rendered HTML will be the same.
Scoped Slots
We can use scoped slots to access data from the child component.
To make data from the child component available in the parent, we can use the v-bind
directive.
A simple example, to get child component data from the parent would be as follows:
src/index.js
:
Vue.component("user", {
data() {
return {
user: {
firstName: "Joe",
lastName: "Smith"
}
};
},
template: `<p>
<slot v-bind:user="user"></slot>
</p>`
});
new Vue({
el: "#app"
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</user>
</div>
<script src="src/index.js"></script>
</body>
</html>
In the code above, we have:
<p>
<slot v-bind:user="user"></slot>
</p>
to make user
available from within the root Vue instance.
Then in the root template, we have:
<user>
<template v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</template>
</user>
to access user
‘s data via slotProps
slotProps
have access to all the data that are made available via v-bind
in the child component.
Abbreviated Syntax for Lone Default Slots
If there’s only the default slot with provided content, we can shorten v-slot:default
or v-slot
on the component as follows:
<user v-slot:default="slotProps">
{{ slotProps.user.firstName }}
</user>
or:
<user v-slot="slotProps">
{{ slotProps.user.firstName }}
</user>
If there’re other named slots, then the syntax above can’t be used because of potential ambiguity.
If we have multiple slots, then we have to write something like the following:
src/index.js
:
Vue.component("user", {
data() {
return {
user: {
firstName: "Joe",
lastName: "Smith"
}
};
},
template: `<p>
<slot v-bind:user="user" name='first-name'></slot>
<slot v-bind:user="user" name='last-name'></slot>
</p>`
});
new Vue({
el: "#app"
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<user>
<template v-slot:first-name="slotProps">
{{ slotProps.user.firstName }}
</template>
<template v-slot:last-name="slotProps">
{{ slotProps.user.lastName }}
</template>
</user>
</div>
<script src="src/index.js"></script>
</body>
</html>
In the code above, we have to name the slots explicitly, and then we can access the child component’s data via v-slot:first-name=”slotProps”
and v-slot:last-name=”slotProps”
respectively.
Also, we wrapped the slot content in template
.
Destructuring Slot Props
We can destructure the properties of the slot props with the destructuring assignment operator.
For example, we can use it as follows:
src/index.js
:
Vue.component("user", {
data() {
return {
user: {
firstName: "Joe",
lastName: "Smith"
}
};
},
template: `<p>
<slot v-bind:user="user" name='first-name'></slot>
<slot v-bind:user="user" name='last-name'></slot>
</p>`
});
new Vue({
el: "#app"
});
index.html
:
<!DOCTYPE html>
<html>
<head>
<title>App</title>
<meta charset="UTF-8" />
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<user>
<template v-slot:first-name="{ user }">
{{ user.firstName }}
</template>
<template v-slot:last-name="{ user }">
{{ user.lastName }}
</template>
</user>
</div>
<script src="src/index.js"></script>
</body>
</html>
In the code above, instead of writing slotProps
, we changed it to { user }
. { user }
is the same as slotProps.user
.
Conclusion
We can use named and scoped slots to create multiple slots and access data from the child component in the parent respectively.
Named slots prevent ambiguity and lets us use multiple slots.
Also, we can use v-bind
in the child component and then use slotProps
in the component to access child component’s data from the parent component.