• Fri, Mar 2026

Working with Vue Slots: Content Distribution Made Simple

Working with Vue Slots: Content Distribution Made Simple

Slots are one of Vue’s most powerful features for content distribution in components. This detailed tutorial explains Vue slots step by step—from default slots to named and scoped slots—with practical code examples and actionable insights. By the end, you’ll confidently design flexible and reusable Vue components using slots like a pro.

Introduction: Why Slots Matter in Vue

Every developer who has worked with Vue knows the magic of reusable components. But what makes them truly flexible is the ability to decide what content goes inside those components. That’s where Vue slots come in. Slots let you inject dynamic content into a component while still keeping the component reusable and maintainable.

When I first discovered slots, it felt like a secret weapon. I no longer had to duplicate components for small variations. Instead, I could build one reusable “shell” and pass different content into it. Think of it like designing a shopping mall: the structure is the same, but each store inside has its own unique layout. Slots give you that level of flexibility in Vue.

What are Vue Slots?

A slot is essentially a placeholder inside a Vue component template that allows the parent to inject its own content. Slots make your components adaptable to different use cases without modifying the component itself.

Basic Analogy

Imagine a picture frame. The frame (component) provides structure and style, but the actual photo inside (slot content) is provided by the user. The frame can be reused with different photos, and each photo still looks great inside it. That’s exactly what Vue slots do for your components.

Getting Started with Default Slots

The simplest slot in Vue is the default slot. It allows a parent component to pass any content to the child component.

Example: Card Component with Default Slot

<!-- Card.vue -->
<template>
 <div >
  <slot>This is fallback content</slot>
 </div>
</template>

<script>
export default {
 name: 'Card'
}
</script>

<style>
.card {
 border: 1px solid #ccc;
 padding: 16px;
 border-radius: 8px;
}
</style>

Using the Card Component

<template>
  <div>
    <Card>
      <h2>Hello World</h2>
      <p>This is some card content.</p>
    </Card>

    <Card>
      <p>Another card with different content.</p>
    </Card>
  </div>
</template>

<script>
import Card from './Card.vue';

export default {
  components: { Card }
}
</script>

Result: Both cards share the same border and styling, but the content inside them differs. This shows how slots help with content distribution in Vue.

Fallback Content in Slots

Slots can have fallback (default) content that renders when no content is provided by the parent. In the Card example above, “This is fallback content” will appear if no child content is passed.

This is extremely useful for providing sensible defaults while keeping your components flexible.

Named Slots: Distributing Multiple Content Areas

Sometimes, one placeholder isn’t enough. You may want to distribute content into multiple sections—like a card with a header, body, and footer. That’s where named slots shine.

Example: Card Component with Named Slots

<!-- Card.vue -->
<template>
 <div >
  <header >
   <slot name="header">Default Header</slot>
  </header>
  <main >
   <slot>Default Body</slot>
  </main>
  <footer >
   <slot name="footer">Default Footer</slot>
  </footer>
 </div>
</template>

Using Named Slots

<Card>
  <template v-slot:header>
    <h2>Custom Card Header</h2>
  </template>

  <p>This is the main card body.</p>

  <template v-slot:footer>
    <button>Action</button>
  </template>
</Card>

Result: Each slot area (header, body, footer) receives its own content, making the component more customizable without duplication.

Scoped Slots: Passing Data from Child to Parent

Scoped slots are like supercharged slots. They let child components pass data back up to the parent, which can then decide how to render it. This makes scoped slots incredibly useful for dynamic layouts, tables, and lists.

Example: List Component with Scoped Slot

<!-- ItemList.vue -->
<template>
  <ul>
    <li v-for="item in items" :key="item.id">
      <slot :item="item">{{ item.name }}</slot>
    </li>
  </ul>
</template>

<script>
export default {
  name: 'ItemList',
  props: ['items']
}
</script>

Using the Scoped Slot

<ItemList :items="users">
  <template v-slot:default="{ item }">
    <strong>{{ item.name }}</strong> - {{ item.email }}
  </template>
</ItemList>

<script>
import ItemList from './ItemList.vue';

export default {
  components: { ItemList },
  data() {
    return {
      users: [
        { id: 1, name: 'Alice', email: 'alice@example.com' },
        { id: 2, name: 'Bob', email: 'bob@example.com' }
      ]
    }
  }
}
</script>

Result: The parent decides how each list item is rendered, while the child provides the data. This decoupling of data and presentation is a game-changer for building flexible components.

Best Practices for Using Vue Slots

  • Keep components flexible: Use slots for customizable content areas instead of hardcoding.
  • Use fallback content wisely: Always provide defaults to prevent broken layouts.
  • Name your slots descriptively:header, body, footer are clearer than slot1, slot2.
  • Scoped slots for data: Pass only the data the parent needs, keeping the API clean.

Advanced Example: Reusable Data Table with Scoped Slots

Let’s build a reusable table component that displays dynamic data but lets the parent decide how each cell is rendered.

DataTable.vue

<template>
  <table border="1" cellpadding="6">
    <thead>
      <tr>
        <th v-for="col in columns" :key="col">{{ col }}</th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="row in data" :key="row.id">
        <td v-for="col in columns" :key="col">
          <slot name="cell" :row="row" :col="col">
            {{ row[col] }}
          </slot>
        </td>
      </tr>
    </tbody>
  </table>
</template>

<script>
export default {
  name: 'DataTable',
  props: {
    columns: Array,
    data: Array
  }
}
</script>

Using DataTable.vue

<DataTable :columns="['id','name','email']" :data="users">
  <template v-slot:cell="{ row, col }">
    <span v-if="col === 'email'">
      <a :href="`mailto:${row[col]}`">{{ row[col] }}</a>
    </span>
    <span v-else>{{ row[col] }}</span>
  </template>
</DataTable>

Result: The parent customizes how emails are rendered as clickable links, while the DataTable handles the structure and layout.

Conclusion: Unlocking the Power of Vue Slots

We’ve journeyed through the world of Vue slots—from simple default slots to powerful scoped slots. Along the way, we saw how slots enable flexible content distribution, reusable components, and clean separation of concerns. Whether you’re building cards, modals, tables, or dashboards, slots are the secret ingredient that makes your Vue components shine.

By mastering slots, you’re not just writing Vue code—you’re designing adaptable, future-proof UI architecture. And trust me, your future self (and your teammates) will thank you for it.

This website uses cookies to enhance your browsing experience. By continuing to use this site, you consent to the use of cookies. Please review our Privacy Policy for more information on how we handle your data. Cookie Policy