React
Named slots in Vue 3: composing components without extra props
Named slots let the parent fill precise regions of a child: the component owns the layout and the page owns the content of each region.
Key points
- The unnamed slot is the default one and receives everything the parent writes between the tags.
- Markup inside
<slot>is the fallback and shows only when the parent passes nothing. - A scoped slot exposes child data to the parent, which decides how to render it.
A named slot is a named hole: the child declares <slot name="header"> and the parent fills it with <template #header>. The component keeps no props and no content logic.
The component that owns the layout
defineProps();
{{ title }}
Content inside <slot> is the fallback: it shows only when the parent passes nothing. The card therefore works on its own and accepts customisation when needed.
Scoped slots: data from child to parent
const rows = [
{ id: 1, name: "Ada", role: "admin" },
{ id: 2, name: "Linus", role: "editor" },
];
{{ row.name }}
The child decides which data to expose, the parent how to show it. That is the difference from a prop: here the structure stays in the child and the rendering in the parent.
Slots or props?
| Case | Winner | Why |
|---|---|---|
| Plain text the child already knows | Prop | Shorter to read in the template |
| Markup with links and icons | Slot | The parent keeps its own components |
| One list rendered differently per page | Scoped slot | Data stays in the child, rendering in the parent |
| A form whose fields the parent controls | Scoped slot | The child holds state, the parent holds the controls |
Common mistakes
- Renaming a slot without updating the parent: the content can vanish with no visible error.
- Putting
#headerandv-sloton the same tag: the compiler reports a syntax error. - Skipping the fallback: with an empty parent you get a hole in the layout.
- Using a slot inside a
v-forwithout:keyon the node outside the list.
The limitation to plan for
Slots have no type contract on their content: TypeScript checks the data of a scoped slot, not what the parent writes inside. A wrong name produces a silent hole, not a compile error.
Where to go next
In the catalogue: shadcn-vue, vue-bits and Ant Design Vue; ready-made layouts are in /blocks. The guides: Composition API and props that do not update. The comparison: Vue vs React, where the same job goes through children.
Frequently asked questions
Can a named slot have a default?
Yes: the content written inside the slot tag in the child is the fallback, and it shows only when the parent passes nothing.
How is this different from props?
Props carry data, slots carry markup. With rich markup, slots keep the parent components out of the child.
What are scoped slots?
Slots that expose child data to the parent through attributes on the slot tag. The parent reads them with v-slot and decides how to render.
Can I have more than one default slot?
No, there is only one default slot. Multiple blocks in the default are concatenated, so distinct regions need distinct names.
Do slots break reactivity?
No. Slot content stays in the parent context and keeps its own reactive dependencies.