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.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs