Vue 3 took the chance and unified both APIs. The less powerful old slots are gone and the old scoped slots are now just called 'slots'. Passing on all slots in Vue 3 is similar (but not exactly equivalent) to passing on scoped slots in Vue 2, so here is the W component from the previous section, adjusted to Vue 3. In this course, you'll learn how to use slots and scoped slots to create flexible and reusable Vue.js components. Slots allow us to pass components and HTML to components, giving us greater control of the appearance than what we get with props. Slots do not replace props. The two features have different purposes. During the course, you'll learn.
I recently figured out how to implement nested slots recursively, including how to do this with scoped slots.
It all started when I wanted to see if I could build a component that replicated the v-for
directive, but only using the template.
To make this story short: I did.
Rendering a list without using the v-for directive or render functions. Supports scoped slots as well... pic.twitter.com/fHlbJdbrEe
— Michael Thiessen (@MichaelThiessen) June 27, 2019(follow me to get my tweets as I learn new stuff!)
And yes, it supports slots and scoped slots — and could be made to support named slots as well.
We can use it like this:
The first one will print out the list of items normally, while the second one will wrap each item in a tag.
It's not a very useful component, but I like to have fun experimenting as I find I learn the most that way.
Let's take a look at what's going on here.
Looping without loops
Normally when we'd want to render out a list of elements or components we'd use the v-for
directive. But we want to get rid of it completely.
So, how do we render out a list of items without using a loop?
Any guesses?
The answer is: recursion.
We can use recursion to represent a list of items.
It's actually not too complicated. I'll show you how this works.
Representing a list recursively
One of my favourite courses in university was Programming Language Concepts.
The most interesting part of it to me was exploring functional programming and logic programming, and seeing the differences to imperative programming (Javascript and most popular languages are imperative).
This was the course that really opened my eyes on how to use recursion, since with pure functional languages everything is recursion.
Anyways, from that course I learned that you can represent a list recursively.
Instead of using an array, each list is a value (the head), and another list (the tail).
So if you wanted to represent the list [1, 2, 3]
, it would be represented recursively as:
We have to end the list somehow, so we use null
instead of another array (an empty array would work too).
Maybe you're starting to see where I'm going with this...
We can use this concept and apply it to our components. Instead, we'll be nesting our components recursively in order to represent our list.
We'll end up rendering something like this. Notice the nested structure of our 'list':
Admittedly, this isn't the exact same thing as what v-for
would render, but that's not exactly the point of this exercise either.
So now that we've worked out what we're doing, how do we create it?
First, I'd like to challenge you to try building it yourself before moving on. I think you'll be able to figure it out, and you'll learn more that way than by just reading how I did it.
Okay, now it's time for me to show how I solved this problem.
Building the component
First, we'll tackle rendering out the list of items recursively. Once we get that working, we'll come back and figure out how to make the slots work.
Using recursion to render lists
Instead of using the recursive list we showed before, we'll work with a plain array:
We have to cover 2 cases here:
- Base case - rendering the first item in the list
- Recursive case - rendering the item, and then the next list
Let's pass [1, 2, 3]
to v-for
:
We want to grab the first item in the list, the 1
, and display it:
Now the component will render out 1
, like we expect it to.
But we can't just render out the first value and stop. We need to render out the value, and then render out the rest of the list as well:
Instead of passing in the whole list
array, we chop off the first item and pass down that new array. We've already printed out the first item, so no need to keep it around.
This is the sequence of things that are happening here:
- We pass
[1, 2, 3]
intov-for
to render - Our
v-for
component renders1
, and then passes[2, 3]
into the nextv-for
to render - Which takes the
[2, 3]
and renders the2
, and then passes[3]
into the nextv-for
- This last
v-for
component then renders out the3
, and we've printed out our list!
The structure of our Vue app looks like this now:
You can see that we have several v-for
components all nested within each other!
One last thing we need to do here though. We need to stop the recursion!
Eventually we run out of items to render, so we need to stop our recursion (or we'll get tons of errors and our computer will melt).
Our next step is get slots working.
Recursively nested slots
We've got the basic component working, but we also want it to work with scoped slots, so we can customize how we render each item:
It's not much extra work to get slots in here, so we'll do that right now!
Nesting slots
Once I figured out how to nest slots recursively, it set off this obsession with slots — which I'm still working through 😅
Things like:
👉 Nesting slots n levels deep
👉 Recursive slots
👉 Wrapping a component to turn one slot into multiple
While making sure that each level can provide defaults and override any slot to maintain flexibility, AND transitions work (trickier than you might think 😅)
If you're looking for some more advanced content on slots, I also did a deep dive into case study that looks at different ways of approaching an architecture problem in Vue with slots.
First we'll take a quick detour into how nested slots work, then we'll show how to incorporate them into our v-for
component.
Any guesses?
The answer is: recursion.
We can use recursion to represent a list of items.
It's actually not too complicated. I'll show you how this works.
Representing a list recursively
One of my favourite courses in university was Programming Language Concepts.
The most interesting part of it to me was exploring functional programming and logic programming, and seeing the differences to imperative programming (Javascript and most popular languages are imperative).
This was the course that really opened my eyes on how to use recursion, since with pure functional languages everything is recursion.
Anyways, from that course I learned that you can represent a list recursively.
Instead of using an array, each list is a value (the head), and another list (the tail).
So if you wanted to represent the list [1, 2, 3]
, it would be represented recursively as:
We have to end the list somehow, so we use null
instead of another array (an empty array would work too).
Maybe you're starting to see where I'm going with this...
We can use this concept and apply it to our components. Instead, we'll be nesting our components recursively in order to represent our list.
We'll end up rendering something like this. Notice the nested structure of our 'list':
Admittedly, this isn't the exact same thing as what v-for
would render, but that's not exactly the point of this exercise either.
So now that we've worked out what we're doing, how do we create it?
First, I'd like to challenge you to try building it yourself before moving on. I think you'll be able to figure it out, and you'll learn more that way than by just reading how I did it.
Okay, now it's time for me to show how I solved this problem.
Building the component
First, we'll tackle rendering out the list of items recursively. Once we get that working, we'll come back and figure out how to make the slots work.
Using recursion to render lists
Instead of using the recursive list we showed before, we'll work with a plain array:
We have to cover 2 cases here:
- Base case - rendering the first item in the list
- Recursive case - rendering the item, and then the next list
Let's pass [1, 2, 3]
to v-for
:
We want to grab the first item in the list, the 1
, and display it:
Now the component will render out 1
, like we expect it to.
But we can't just render out the first value and stop. We need to render out the value, and then render out the rest of the list as well:
Instead of passing in the whole list
array, we chop off the first item and pass down that new array. We've already printed out the first item, so no need to keep it around.
This is the sequence of things that are happening here:
- We pass
[1, 2, 3]
intov-for
to render - Our
v-for
component renders1
, and then passes[2, 3]
into the nextv-for
to render - Which takes the
[2, 3]
and renders the2
, and then passes[3]
into the nextv-for
- This last
v-for
component then renders out the3
, and we've printed out our list!
The structure of our Vue app looks like this now:
You can see that we have several v-for
components all nested within each other!
One last thing we need to do here though. We need to stop the recursion!
Eventually we run out of items to render, so we need to stop our recursion (or we'll get tons of errors and our computer will melt).
Our next step is get slots working.
Recursively nested slots
We've got the basic component working, but we also want it to work with scoped slots, so we can customize how we render each item:
It's not much extra work to get slots in here, so we'll do that right now!
Nesting slots
Once I figured out how to nest slots recursively, it set off this obsession with slots — which I'm still working through 😅
Things like:
👉 Nesting slots n levels deep
👉 Recursive slots
👉 Wrapping a component to turn one slot into multiple
While making sure that each level can provide defaults and override any slot to maintain flexibility, AND transitions work (trickier than you might think 😅)
If you're looking for some more advanced content on slots, I also did a deep dive into case study that looks at different ways of approaching an architecture problem in Vue with slots.
First we'll take a quick detour into how nested slots work, then we'll show how to incorporate them into our v-for
component.
If you know how to use slots normally, I think you'll get this pretty quick.
Let's say we have three components, a Parent
, a Child
, and a Grandchild
. We want to pass some content from the Parent
component and have it rendered in the Grandchild
.
All we're doing is passing the slot from the Parent
to the Child
like we normally do, and then again from the Child
to the Grandchild
.
Starting with the Parent
, we pass in some content:
We do some things in our Child
component — which we'll get to in just a moment. And then our Grandchild
component takes the slot and renders out the content:
So what does this Child
component look like?
We need it to take the content from Parent
and provide it to the Grandchild
component, so we're connecting two different slots together here:
Remember, the element renders out content that's passed to the component as a slot. So we're taking that content from
Parent
and then rendering it inside of the slot of Grandchild
.
Adding in our scoped slots
The only thing different with nesting scoped slots is that we also have to pass along the scoped data.
Adding this into our v-for
we now get this:
Let's look at our base case first.
If no slot is provided, we default to what's inside of the element, and render
list[0]
just like before. But if we do provide a slot, it will render it out and pass the list item to the parent through the slot scope.
The recursive case here is similar. If we pass in a slot to v-for
, it will render it within the slot of the next v-for
, so we get our nesting. It also grabs the item
from the scoped slot and passes that back up the chain too.
And now, we have a component that imitates (more or less) a v-for
, but only using the template!
Vue Component Slot
Wrapping up
We went through a long and winding path to get here, but now we've seen how to create a component that replicates a v-for
, but only using the template.
Vue Slot Name
We covered:
- representing lists recursively
- recursive components
- nested slots and nested scoped slots (or recursive slots if prefer calling them that)
If you enjoyed this article, please share it with others who may enjoy it as well!
Vue Slot Scope Not Working
And sign up for my email list below if you want to get advanced Vue content like this every week.