How to Use Named Slots in Vue

As the creator of CoreUI and with over 25 years of software development experience, I’ll show you how to use named slots to create flexible and reusable component layouts.

Named slots allow you to define multiple content insertion points in a component, giving you precise control over where different pieces of content are rendered.

<!-- Card component with named slots -->
<template>
  <div class="card">
    <div class="card-header">
      <slot name="header">
        <!-- Default header content -->
        <h3>Default Title</h3>
      </slot>
    </div>

    <div class="card-body">
      <slot>
        <!-- Default slot (unnamed) -->
        <p>Default content goes here</p>
      </slot>
    </div>

    <div class="card-actions">
      <slot name="actions">
        <!-- Default actions -->
        <button>Default Action</button>
      </slot>
    </div>

    <div class="card-footer" v-if="$slots.footer">
      <slot name="footer"></slot>
    </div>
  </div>
</template>

<script setup>
// Component logic here
</script>
<!-- Using the Card component with named slots -->
<template>
  <div>
    <!-- Example 1: Using all named slots -->
    <Card>
      <template #header>
        <h2>Custom Header</h2>
        <p>Subtitle here</p>
      </template>

      <template #default>
        <p>This content goes in the main slot</p>
        <ul>
          <li>Item 1</li>
          <li>Item 2</li>
        </ul>
      </template>

      <template #actions>
        <button @click="handleSave">Save</button>
        <button @click="handleCancel">Cancel</button>
      </template>

      <template #footer>
        <small>Last updated: {{ lastUpdated }}</small>
      </template>
    </Card>

    <!-- Example 2: Using v-slot syntax -->
    <Card>
      <template v-slot:header>
        <h2>Another Card</h2>
      </template>

      <p>Content without template wrapper</p>

      <template v-slot:actions>
        <button>Action Button</button>
      </template>
    </Card>

    <!-- Example 3: Conditional slots with scoped data -->
    <UserCard>
      <template #avatar="{ user }">
        <img :src="user.avatar" :alt="user.name" />
      </template>

      <template #content="{ user }">
        <h3>{{ user.name }}</h3>
        <p>{{ user.email }}</p>
      </template>
    </UserCard>
  </div>
</template>

<script setup>
import Card from './Card.vue'
import UserCard from './UserCard.vue'

const lastUpdated = new Date().toLocaleDateString()

const handleSave = () => {
  console.log('Saving...')
}

const handleCancel = () => {
  console.log('Cancelled')
}
</script>

Named slots use the name attribute in the child component and #slotName or v-slot:slotName syntax in the parent. The # symbol is shorthand for v-slot:. Use $slots.slotName to check if a slot has content. Named slots can also pass data to the parent using scoped slots. The default slot doesn’t need a name and can be accessed with #default.

Best Practice Note:

In CoreUI components, we extensively use named slots to create flexible layouts for cards, modals, and data tables. This pattern allows developers to customize specific sections while maintaining consistent structure and styling across applications.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author