Named slots let you put different content in specific places inside a component. Scoped slots let the component share data with the content you put inside it.
Named slots and scoped slots in Vue
<template> <slot name="slotName">Default content</slot> </template> <!-- Using named slot --> <ChildComponent> <template #slotName> Custom content here </template> </ChildComponent> <!-- Scoped slot syntax --> <ChildComponent> <template #slotName="slotProps"> {{ slotProps.someData }} </template> </ChildComponent>
Named slots use the name attribute to identify different slots.
Scoped slots pass data from the child component to the parent using slot props.
<!-- ChildComponent.vue --> <template> <header><slot name="header">Default Header</slot></header> <main><slot>Default Body</slot></main> <footer><slot name="footer">Default Footer</slot></footer> </template>
<!-- Using ChildComponent with named slots -->
<ChildComponent>
<template #header>
<h1>My Custom Header</h1>
</template>
<p>This is the main body content.</p>
<template #footer>
<small>Custom footer text</small>
</template>
</ChildComponent>itemText to the slot as a prop named text.<!-- ChildComponent.vue with scoped slot --> <template> <slot name="item" :text="itemText"></slot> </template> <script setup> const itemText = 'Hello from child' </script>
text from the child and displays it inside the slot content.<!-- Using scoped slot -->
<ChildComponent>
<template #item="{ text }">
<p>Received: {{ text }}</p>
</template>
</ChildComponent>This example shows a card component with named slots for header and footer. The parent fills these slots with custom content and uses the default slot for the main body.
<!-- Card.vue --> <template> <div class="card"> <header class="card-header"> <slot name="header">Default Header</slot> </header> <section class="card-body"> <slot>Default Body Content</slot> </section> <footer class="card-footer"> <slot name="footer">Default Footer</slot> </footer> </div> </template> <style scoped> .card { border: 1px solid #ccc; padding: 1rem; border-radius: 0.5rem; max-width: 300px; } .card-header, .card-footer { background: #f0f0f0; padding: 0.5rem; } .card-body { padding: 1rem 0; } </style> <!-- App.vue --> <template> <Card> <template #header> <h2>Welcome!</h2> </template> <p>This is the main card content.</p> <template #footer> <button @click="alert('Clicked!')">Click me</button> </template> </Card> </template> <script setup> import Card from './Card.vue' </script>
Always provide default slot content to keep your component usable without custom slots.
Use v-bind or : to pass multiple props in scoped slots.
Named slots help organize complex components with multiple customizable parts.
Named slots let you put different content in specific places inside a component.
Scoped slots let the child component share data with the slot content.
Use named and scoped slots to build flexible, reusable Vue components.