What Causes This Warning?
This warning occurs when you invoke a slot function outside of the render function or template rendering context. Slots should only be called during rendering.
The Problem
export default {
setup(props, { slots }) {
// ❌ Calling slot in setup
const content = slots.default?.()
// ❌ Calling slot in lifecycle hook
onMounted(() => {
const header = slots.header?.()
console.log(header)
})
}
}
The Fix
Call Slots in Render Function
export default {
setup(props, { slots }) {
// ✅ Return render function that calls slots
return () => {
return h('div', [
slots.header?.(),
slots.default?.(),
slots.footer?.()
])
}
}
}
With Template (Automatic)
<template>
<!-- ✅ Slots are invoked automatically in templates -->
<div>
<header>
<slot name="header" />
</header>
<main>
<slot />
</main>
</div>
</template>
Common Scenarios
Checking Slot Content
// ❌ Wrong - invoking slot to check content
export default {
setup(props, { slots }) {
const hasHeader = slots.header?.().length > 0
return () => h('div', hasHeader ? slots.header() : null)
}
}
// ✅ Correct - check slot existence, invoke in render
export default {
setup(props, { slots }) {
return () => {
const hasHeader = !!slots.header
return h('div', [
hasHeader ? h('header', slots.header()) : null,
slots.default?.()
])
}
}
}
Conditional Slot Rendering
export default {
setup(props, { slots }) {
const showHeader = ref(true)
// ✅ All slot calls inside render
return () => h('div', [
showHeader.value && slots.header
? h('header', slots.header())
: null,
slots.default?.()
])
}
}
Slot with Fallback
export default {
setup(props, { slots }) {
return () => h('div', [
// ✅ Fallback in render function
slots.header?.() ?? h('h1', 'Default Header'),
slots.default?.() ?? h('p', 'No content provided')
])
}
}
Scoped Slots
export default {
props: ['items'],
setup(props, { slots }) {
return () => h('ul', props.items.map((item, index) =>
h('li', [
// ✅ Pass slot props during render
slots.default?.({ item, index }) ?? item.name
])
))
}
}
Logging Slot Content
// ❌ Can't log slot VNodes
export default {
setup(props, { slots }) {
console.log(slots.default?.()) // Warning!
}
}
// ✅ Log in render or use slot check
export default {
setup(props, { slots }) {
console.log('Has default slot:', !!slots.default)
return () => {
const content = slots.default?.()
console.log('Rendered slot content') // OK during render
return h('div', content)
}
}
}
Wrapper Component
export default {
setup(props, { slots, attrs }) {
return () => h(
'div',
{ class: 'wrapper', ...attrs },
// ✅ Forward slots in render
slots.default?.()
)
}
}
Computed Based on Slots
export default {
setup(props, { slots }) {
// ✅ Check slot existence (not invoke)
const hasCustomFooter = computed(() => !!slots.footer)
return () => h('div', [
slots.default?.(),
hasCustomFooter.value
? h('footer', slots.footer())
: h('footer', 'Default Footer')
])
}
}
Quick Checklist
- Only call slots inside render function
- Use
!!slots.nameto check existence (not invoke) - Templates handle slot invocation automatically
- Scoped slots: pass props when calling
slots.name({ props }) - Use
slots.default?.()for safe invocation - Fallbacks:
slots.name?.() ?? fallbackContent