Vue.js Slots: The Magic Gift Boxes đ
The Story of the Magic Mailbox
Imagine you have a magic mailbox. This mailbox is special because whatever you put inside it appears somewhere elseâinside a friendâs house, exactly where they want it!
In Vue.js, Slots work just like this magic mailbox. A parent component can send content (like letters, gifts, or surprises) through slots, and the child component decides where to display them.
What Are Slots?
Think of a slot as a placeholder or an empty box inside a component that says:
âHey! Put something here, please!â
The parent component fills that box with whatever content it wants.
Simple Example
Child Component (MyCard.vue):
<template>
<div class="card">
<slot></slot>
</div>
</template>
Parent Using It:
<MyCard>
<p>Hello! I'm inside the card!</p>
</MyCard>
Result:
<div class="card">
<p>Hello! I'm inside the card!</p>
</div>
The <slot> tag is like saying: âWhatever the parent puts between my tags goes HERE.â
Slot Content
Slot Content is whatever you place between a componentâs opening and closing tags.
graph TD A["Parent Component"] -->|sends content| B["slot tag"] B --> C["Content Appears Here"]
Real Example
<AlertBox>
<strong>Warning!</strong>
<p>Your ice cream is melting!</p>
</AlertBox>
Everything between <AlertBox> and </AlertBox> is the slot content. It gets inserted where the childâs <slot> lives.
Slot Fallback Content
What if no one sends anything to your mailbox? You can have a default message waiting!
Fallback Content is what shows up when the parent doesnât provide any slot content.
Example
Child (SubmitButton.vue):
<template>
<button>
<slot>Submit</slot>
</button>
</template>
Parent Option 1 - No content:
<SubmitButton></SubmitButton>
<!-- Shows: Submit -->
Parent Option 2 - Custom content:
<SubmitButton>Save Changes</SubmitButton>
<!-- Shows: Save Changes -->
Think of it like a greeting card. The card says âHappy Birthdayâ by default, but you can write your own message!
The v-slot Directive
The v-slot directive is your remote control for slots. It lets you:
- Target specific slots by name
- Receive data from slots (scoped slots)
Basic Syntax
<template v-slot:default>
Content goes here
</template>
Shorthand: The # Symbol
Just like @ is short for v-on, the # is short for v-slot:
<template #default>
Content goes here
</template>
Named Slots
What if your mailbox has multiple compartments? One for letters, one for packages, one for magazines?
Named Slots let you have multiple slots with different names!
Example
Child (PageLayout.vue):
<template>
<div class="page">
<header>
<slot name="header"></slot>
</header>
<main>
<slot></slot>
</main>
<footer>
<slot name="footer"></slot>
</footer>
</div>
</template>
Parent Using It:
<PageLayout>
<template #header>
<h1>My Website</h1>
</template>
<p>Main content goes here!</p>
<template #footer>
<p>Copyright 2024</p>
</template>
</PageLayout>
graph TD A["#header"] --> B["Header Section"] C["Default Slot"] --> D["Main Section"] E["#footer"] --> F["Footer Section"]
Key Points:
<slot name="header">= named slot<slot>without name = default slot- Use
#slotNameto target specific slots
Dynamic Slot Names
Sometimes you donât know which slot to use until your app is running. Thatâs where Dynamic Slot Names come in!
Use square brackets [ ] to make slot names dynamic:
<template #[dynamicSlotName]>
Content for dynamic slot
</template>
Example
<script setup>
import { ref } from 'vue'
const currentSlot = ref('header')
</script>
<template>
<PageLayout>
<template #[currentSlot]>
<p>I appear in: {{ currentSlot }}</p>
</template>
</PageLayout>
</template>
Now changing currentSlot to 'footer' moves your content to the footer slot!
Think of it like a magic envelope that changes which mailbox compartment it goes to!
Scoped Slots
Hereâs where slots get superpowers! đڏ
Normal slots are like a one-way mailboxâparent sends, child receives.
Scoped Slots are a two-way street! The child can send data back to the parent through the slot.
The Problem
Imagine a list component. The child has the data, but the parent wants to decide HOW each item looks.
The Solution: Scoped Slots
Child (FancyList.vue):
<template>
<ul>
<li v-for="item in items" :key="item.id">
<slot :item="item" :index="index">
{{ item.name }}
</slot>
</li>
</ul>
</template>
Parent Using It:
<FancyList :items="fruits">
<template #default="slotProps">
<span>{{ slotProps.item.name }}</span>
<span>đ</span>
</template>
</FancyList>
graph LR A["Child Component"] -->|sends item data| B["Scoped Slot"] B -->|parent accesses| C["slotProps.item"]
The child says: âHereâs my data. You decide how to display it!â
Slot Props
Slot Props are the pieces of data that a child component passes to its slot.
How to Pass Slot Props
Bind them on the <slot> tag like regular attributes:
<slot
:user="currentUser"
:isAdmin="true"
message="Hello!"
></slot>
How to Receive Slot Props
Use v-slot with a variable name:
<template #default="props">
<p>{{ props.user.name }}</p>
<p>{{ props.message }}</p>
</template>
Destructuring Slot Props
Make your code cleaner by destructuring:
<template #default="{ user, message }">
<p>{{ user.name }}</p>
<p>{{ message }}</p>
</template>
Complete Example
Child (UserCard.vue):
<template>
<div class="user-card">
<slot
:user="userData"
:lastLogin="lastLoginDate"
:isOnline="online">
</slot>
</div>
</template>
<script setup>
const userData = { name: 'Alex', age: 25 }
const lastLoginDate = '2024-01-15'
const online = true
</script>
Parent:
<UserCard>
<template #default="{ user, isOnline }">
<h3>{{ user.name }}</h3>
<span :class="isOnline ? 'green' : 'gray'">
{{ isOnline ? 'đ˘ Online' : '⍠Offline' }}
</span>
</template>
</UserCard>
Quick Summary
| Concept | What It Does |
|---|---|
| Slot | Placeholder for parent content |
| Slot Content | Content placed between component tags |
| Fallback Content | Default content if nothing provided |
| v-slot | Directive to target slots |
| Named Slots | Multiple slots with unique names |
| Dynamic Slot Names | Slot names from variables |
| Scoped Slots | Child passes data to parent |
| Slot Props | Data passed through scoped slots |
The Magic Formula
graph TD A["Parent Component"] -->|1. Provides Content| B["Slot"] B -->|2. Passes Props| C["Scoped Slot"] C -->|3. Renders Custom Content| D["Final Output"]
Remember:
- Slots = Content placeholders
- Named Slots = Multiple placeholders
- Scoped Slots = Two-way data street
- Slot Props = Data from child to parent
Now you have magic mailboxes everywhere in your Vue app! Fill them with whatever content you want, receive data back, and create flexible, reusable components.
Youâre now a Slot Master! đ
