Vue 3 Fundamentals

Slots for Layout

Ben Hong

Ben Hong

Vue 3 Fundamentals

Check out a free preview of the full Vue 3 Fundamentals course

The "Slots for Layout" Lesson is part of the full, Vue 3 Fundamentals course featured in this preview video. Here's what you'd learn in this lesson:

Ben demonstrates using slots to pass a template fragment to a child component and let the child component render the fragment within its template. Props and if-else conditions can be attached to slots to allow for more flexibility and reusability.


Transcript from the "Slots for Layout" Lesson

>> We've talked a lot about props. We talked about emits. And so something you might be thinking though is that, well, okay, this makes sense, right? I can pass data down to my components, but at some point this might get kind of cumbersome. And so I wanna kinda give you a micro version of the button exercise that I give in the Production Grade Vue workshop, so if you want to check that out for this.

But let's, for a second, think of a component that's going to pick this out real quick as a base button. And so for this base button, you probably are thinking, well, it's a button component. And what kind of props that I wanna pass to it? Well, if we think about it, we can say, well, usually, you need to have some texts on your button.

So okay, let's call that a text prop, and then I'll do the shorthand typing right now. So this is an alternative, by the way, for prop where if you don't wanna do the required and default, you just pass the title directly like this. But again, generally speaking, I think it's a best practice to define whether it's required or if it's not required and has the default, basically do your due diligence on that, it helps your team in the long run.

Okay, well, there might be some text here. And then what happens if your product manager is like, you know what, I think I want to use the icons on the left hand side, then you're like okay, maybe like icon left string or maybe just icon, right, cuz you're okay, as an icon.

As an icon, it's going to the left. And then they go, well, actually depends cuz the icon might be on the left or the right. And then you might start getting the hint of where I'm going with this when you're like, okay, well I can write, and for now I'm just keeping it simple just keeping these strings.

So it's like okay string. And then all of a sudden it's like, well, I also need this to replace a loading spinner, in case it's like fetching an API call. And all of a sudden, what you got here is this massive list of props for what is really just a button component, right?

Especially for something like this text here, can you imagine how complicated the V-if else-if is gonna be when it's like if floating shows this but else this but then also there's an icon then do this, like this button will become very, very troublesome to manage. And so of course, Vue has an answer for this.

And the answer for this is around the idea of slots. And so slots are an interesting concept in that we have been using them this whole time when it comes to HTML. So how does a slot work? Well, let's go and just show you how. Let's just refactor this real quick.

So icon, let's do this. Icon text. So I just wanna have something that will render on the page, And then, we'll say, okay, so let's go. Actually, since this is a playground, let me move the base button to playground so that this can be consistent. Base button is going to source, going to components.

There we go, perfect. And then to keep that consistent, we'll just do base button and kebab case. We'll hide that. And then now, let's make sure I'm running the playground app, so we don't run the wrong one. Cuz nothing's more infuriating than thinking being in the wrong environment and thinking your code is not changing.

Okay, so we have a base button here, App.vue. So import BaseButton. BaseButton from here. Base and button. And then this is specifically BaseCounter, Okay, how does BaseButton work? At the moment it is BaseButton, and we can give it a text of Hi, and the icon of Arrow, okay.

So what happened up, I think I did not close it. There we go. Okay, we see it here, right? It says Arrow and Hi, great. What we wanna avoid when it comes to props is being overly descriptive with how something is supposed to be used. It's useful for guidance, it's useful as a guardrail.

But when it comes to something like what text belongs in a button, it doesn't often make sense to do something like that with a prop. So instead, what do we do? Well, there is a built-in element called slot. And so it's globally available to your app at all times because this is a Vue component.

And slot, if we think about it, it actually functions just as you would expect when it comes to HTML, you just didn't realize it. So rather than being like, I wanted to say Arrow and then dash Hi, this is a BaseButton. And we call it like a normal component, and we say, Arrow Left and Hi.

And if you're looking at this going like, isn't that HTML? I'd say exactly, it's just like HTML. [LAUGH] Why complicate things beyond that, right? There are times where you just wanna let people just define whatever they want inside of your component. Now granted, eventually you could do some fancy things regarding maybe some props, maybe there is an a point where you have some base icon here, for example.

But slots are one of the most powerful features of Vue in the sense that it allows for flexibility, it's almost like in that regard the complete opposite of props. Where props you're being very prescriptive, do this with this type this is required to do it in this place.

Slots is saying, all right, I'm open, like you basically work with me here. And so the cool thing about slots too, is that slots are more intelligent than a standard HTML element in that you can actually provide it a default content. So we can do is we can say, yes, so we'll just call it submit by default.

And so, when you do that, this means that when I go ahead and delete what's inside, you'll notice it automatically falls back to that content. Which again, can be very nice for just sort of providing some conventions, especially when people start having design systems and that sort of thing.

So okay, most of our buttons are submit, we can go ahead and say, okay, all BaseButton have a default of submit, but otherwise the moment someone needs to override it, easy-peasy, this is no longer submit, this is now cancel. As you can see, updates, just as what you would expect.

>> And you can bind that, obviously, to your data or-
>> Yep, exactly. So the question here is around, can we do more than this? Yeah, because we should treat this just like any other HTML element. So in here, we can do something like, And there you go.

It just works. That is one of the things that always excites me when I look at how Vue API is designed is extensibility on what people already understand, and then just leveraging the heck out of it, why not? Why not sprinkle on some syntactic sugar, some superpowers that make it even easier, right?

Because, again, slots inherently already work from a HTML perspective but now you can provide default content, we can actually do if conditions for slots too. So if you had something like called direction, and let's say direction's here, and we say v-if left. We could use slot for v-else.

And then, so let's say the default is actually this Submit. But otherwise, if it has a configured prop of left, it might be like Icon Left, whatever, Cancel. And so what you got here is I'm really overriding it. But if we come in this year, save you see that submit is falling back as we will expect, but if we have, opps, I think my v-if direction doesn't make sense, actually I can just do left, that's fine.

So I do left, is a Boolean and the default is false. So like that, you see Submit does come up. And then we can say left is true. Icon left cancel. So that's pretty cool. But what else can we do? There's even more because if we think of slots like a mailbox slot, we might wanna have multiple slots on a component, not just one.

And so what if we were to do something like layouts? Well, if you had a layout component, Let's say I'm gonna do base-layout.vue. What you're probably gonna end up having is you probably have some sort of div that is your class wrapper, right, assuming you have a design system, but then you might wanna start figuring out where things are being slotted.

So for example, you might have an aside here, that is class like sidebar. And then you probably have somewhere where you wanna render out your main content. And then you probably have your footer content. And so again, if you do this by traditional props and that kind of stuff, it gets really painful because how do you pass components into props, that's a whole messy matter.

But what we can do is we can give our slots names. So this can be the sidebar slot. This can be the main slot. And this can be the footer slot. And so the way this would work is, if we're inside of our App.vue, we can go ahead and import base.

Let's see, let me duplicate this one, and then bom bom, BaseLayout from BaseLayout. We organize that to be alphabetical, great. Okay, then we get our BaseLayout. What we can do now is we can now define specific snippets of HTML to go in specific places. How do we define HTML?

We have our template element. And so I know it's like, whoa, there's a template in a template. But again, if you look at MDN, template is an actual HTML element. It's a content template element, this is a thing. I think a lot of people at one point will say, that looks like a Vue thing.

No, this is in the MDN docs. This is fairly standard. And so what we can do is we can say, look, this is the aside. And how do we tell the template that we wanna go there? Well, we do the v-slot directive, and we tell it the, I believe this is Aside, like this.

And so if this renders correctly, base layout. [SOUND] Wait, I did not, I call this sidebar. So, of course, there's nowhere to go, so I misnamed it sidebar Aside. There we go. And so let's give this a little bit of styling just so that we can see the difference.

So let's go ahead and give a sidebar border 2px solid red. And then we'll go ahead and do the same for each one, so we'll have main, and then we'll have footer. And so we'll make this green, we'll make this blue. And so you can see right now because nothing's being passed to the slot for main or footer, nothing's being rendered just the Aside.

And so all we got to do now is we can go ahead and add things like template, v-slot main. And then I can even do things like actually render the user card. Actually, I could even just take this component right here. Let's take the user card here,and just drop it directly inside of main.

And so you can see here that it's actually being rendered inside. Although where did my borders go? Main class. I didn't give it a class. Class main. There we go. Did you have a question?
>> I was wondering if the components that are not the slots rendering the DOM when you bring that template in.

>> So you're saying that if I don't-
>> So currently you have nothing in the footer slot or a footer element.
>> Good question, and the answer is, yes, it is there. Because as far as Vue is concerned, what you're saying is this is the scaffold specter to render, there's nothing.

Well, Yeah, I guess it would be a little bit tricky to as far as like checking make sure the slot like whether the slot is empty or not, so at that point, though, I would say that, if you started to get conditional about what content is showing insight, at that point, it would make more sense to make a slot name of a slot a footer, for example.

And then maybe the default is to have some content in here, but then otherwise, let someone completely overwrite it and then that way, if there is no slot, nothing gets shows up at all. That's probably the way I would approach it in that particular case. So here we go.

We have that, what did I forget? There we go. Boom. So we can see now the footer actually is rendering, and so all I got to do now is do the template V slot footer inside of here, and then let's drop the base counter in there. Boom. And so slots become really powerful because if you've ever worked with like a backend CMS where users are defining layouts, now you have the ability to, like when you're fetching data to actually drop things where they're supposed to go.

As opposed to having to configure, this is my two column layout, this is my three column layout, this is my two column with one. Like, there comes a point where that becomes very tedious to maintain. And slots do a lot in terms of opening up a lot of reusability and composability when it comes to that.

What's cool about this too is that because it's programmatic in terms of assigning its property, there's no, like it doesn't have to appear in a certain order. So for example, I'll just swap these to show that. You'll see that even though technically inside of here, we've ordered it as like a side main, whatever is totally being controlled at this level.

And so as a result, you can again it says, allows for more composability and flexibility when it comes to that. And the other thing I wanna leave you off with before we dive into exercises and other questions is that if you're looking at some other code bases, not everyone writes out the V slot directive.

And this is a convention within Vue 3, and that you can shorthand it to just the hashtag symbol. The way to think about it is like the idea of the slot that you want to put the template stuff in. To be honest, I find it a little bit confusing because if someone doesn't know what this is and tries to Google this, sometimes they might end up with CSS stuff, something like this shorthand.

I personally, I don't tend to use it again, it is in code basis which is why I'm telling you all this exists. So if that's your team's convention, I would say roll with it. Personally, though, I don't mind actually being explicit about slot sidebar, I think this to me is expressive and I get, yeah, personally, that's what I prefer.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now