JAVASCRIPT
Empower Components with Flexible Content using Vue 3 Scoped Slots
Discover how Vue 3 scoped slots enable parent components to pass not just data, but entire content blocks and even access child component data for highly flexible and reusable UI.
<!-- MyButton.vue (Child Component) -->
<template>
<button :class="type">
<slot :isLoading="loading" :label="buttonLabel">
<!-- Default content if no slot content is provided -->
Click Me
</slot>
</button>
</template>
<script setup>
import { ref } from 'vue';
import { defineProps } from 'vue';
defineProps({
type: {
type: String,
default: 'primary'
}
});
const loading = ref(false);
const buttonLabel = ref('Action');
// Simulate async operation for demo
setTimeout(() => {
loading.value = true;
buttonLabel.value = 'Loading...';
}, 2000);
</script>
<style scoped>
button {
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
.primary {
background-color: #007bff;
color: white;
}
.secondary {
background-color: #6c757d;
color: white;
}
</style>
<!-- App.vue (Parent Component) -->
<template>
<div>
<h1>Scoped Slots Example</h1>
<h2>Default Slot Content:</h2>
<MyButton type="primary"></MyButton>
<h2>Custom Slot Content:</h2>
<MyButton type="secondary">
<span>Submit Form</span>
</MyButton>
<h2>Scoped Slot with Child Data:</h2>
<MyButton type="primary" @click="doSomething">
<template #default="slotProps">
<span v-if="slotProps.isLoading">
<span class="spinner"></span> {{ slotProps.label }}
</span>
<span v-else>
Custom {{ slotProps.label }} Button
</span>
</template>
</MyButton>
</div>
</template>
<script setup>
import MyButton from './MyButton.vue'; // Assuming MyButton.vue is in the same directory
function doSomething() {
alert('Button clicked!');
}
</script>
<style>
.spinner {
display: inline-block;
width: 1em;
height: 1em;
border: 2px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: #fff;
animation: spin 1s ease-in-out infinite;
-webkit-animation: spin 1s ease-in-out infinite;
vertical-align: middle;
margin-right: 5px;
}
@keyframes spin {
to { -webkit-transform: rotate(360deg); }
}
@-webkit-keyframes spin {
to { -webkit-transform: rotate(360deg); }
}
</style>
How it works: This snippet demonstrates Vue 3's scoped slots, a powerful feature for creating highly reusable and customizable components. The `MyButton` component defines a `<slot>` that can receive content from its parent. By binding `isLoading` and `label` to the slot, the child component makes its internal state available to the parent. The parent (`App.vue`) then uses `<template #default="slotProps">` to access these slot props and conditionally render content (e.g., a spinner and dynamic text), allowing for flexible UI composition where the child defines the structure, and the parent defines specific content and behavior.