The # Syntax for Named Slots
Vue's template syntax has a shorthand for named slots that many developers overlook. It does not save many keystrokes, but it improves clarity when you are working with components that expose multiple slots.
The standard way to pass content to a named slot uses the v-slot directive.
<Todo>
<template v-slot:description>
<Item />
</template>
</Todo>
The shorthand replaces v-slot: with #.
<Todo>
<template #description>
<Item />
</template>
</Todo>
The difference is small on its own. It becomes more meaningful as the template grows.
Why It Matters
Components with multiple named slots benefit the most. Consider a layout component with slots for header, sidebar, main, and footer.
<Layout>
<template v-slot:header>
<HeaderContent />
</template>
<template v-slot:sidebar>
<SideNav />
</template>
<template v-slot:main>
<PageContent />
</template>
<template v-slot:footer>
<FooterContent />
</template>
</Layout>
With the shorthand:
<Layout>
<template #header>
<HeaderContent />
</template>
<template #sidebar>
<SideNav />
</template>
<template #main>
<PageContent />
</template>
<template #footer>
<FooterContent />
</template>
</Layout>
The difference is visual clarity. The # symbol reads as a marker for slots, similar to how @ marks events and : marks bindings in Vue templates. Consistency across these prefixes helps the eye parse the template structure faster.
Dynamic Slot Names
Vue also supports dynamic slot names using bracket notation.
<template #[dynamicSlotName]>
<SomeContent />
</template>
This works with both v-slot: and # syntax. It is useful when rendering slot content based on configuration or when building generic wrapper components.
<GenericWrapper>
<template v-for="slotName in availableSlots" :key="slotName" #[slotName]>
<DynamicContent :name="slotName" />
</template>
</GenericWrapper>
Scoped Slots with the Shorthand
Named slots often pass data back to the parent. The shorthand handles this as well.
<List :items="items">
<template #item="{ item, index }">
<div class="list-item">
<span>{{ index }}.</span>
<span>{{ item.label }}</span>
</div>
</template>
</List>
The destructuring in the slot parameter follows the same pattern as JavaScript function parameters. You pull out only the values you need.
When the Default Slot is Enough
Not every component needs named slots. If a component only passes content through one slot, the default (unnamed) slot is simpler.
<Button @click="handleClick">
Click me
</Button>
Introducing named slots adds complexity. Use them when the component truly has multiple insertion points. Over engineering slot APIs makes components harder to use, not more flexible.
Summary
- The
#shorthand replacesv-slot:for cleaner templates. - Multiple named slots benefit the most from the shorthand.
- Dynamic slot names work with both syntaxes.
- Scoped slots pass data through the same shorthand.
- Use default slots for simple components and named slots for components with multiple insertion points.
Small patterns like this compound across a codebase. A team that consistently uses the shorthand will find slot boundaries easier to spot during code reviews and debugging.
Top comments (0)