Fix: Non-Function Value Encountered for Slot in Vue.js

Error message:
Non-function value encountered for slot "{key}".
Components 2025-01-25

What Causes This Warning?

This warning occurs in render functions when you pass a non-function value where a slot function is expected. In Vue 3, slots should be functions that return VNodes.

The Problem

import { h } from 'vue'

// ❌ String instead of function
h(MyComponent, null, {
  default: 'text content'
})

// ❌ VNode array instead of function
h(MyComponent, null, {
  header: [h('h1', 'Title')]
})

The Fix

Use Functions for Slots

import { h } from 'vue'

// ✅ Function returning VNodes
h(MyComponent, null, {
  default: () => 'text content',
  header: () => h('h1', 'Title')
})

// ✅ Function returning array of VNodes
h(MyComponent, null, {
  default: () => [
    h('p', 'First paragraph'),
    h('p', 'Second paragraph')
  ]
})

Common Scenarios

Basic Slot

// ❌ Wrong
h(Card, null, {
  default: h('p', 'Content')
})

// ✅ Correct
h(Card, null, {
  default: () => h('p', 'Content')
})

Named Slots

// ✅ Multiple named slots
h(Layout, null, {
  header: () => h('h1', 'Page Title'),
  default: () => h('main', 'Main content'),
  footer: () => h('footer', 'Copyright 2024')
})

Scoped Slots

// ✅ Scoped slot with props
h(List, { items }, {
  default: ({ item, index }) => h('li', `${index}: ${item.name}`)
})

Dynamic Slots

// ✅ Dynamic slot content
const slots = {
  default: () => showContent.value
    ? h('div', 'Content')
    : h('div', 'No content'),
  header: () => h('h1', title.value)
}

h(Component, null, slots)

JSX Alternative

// ✅ JSX handles this automatically
<MyComponent>
  {{
    default: () => <p>Content</p>,
    header: () => <h1>Title</h1>
  }}
</MyComponent>

// Or children syntax for default slot
<MyComponent>
  <p>This is default slot content</p>
</MyComponent>

Template vs Render Function

<!-- Template (simpler) -->
<template>
  <MyComponent>
    <template #header>
      <h1>Title</h1>
    </template>
    <p>Default content</p>
  </MyComponent>
</template>

<!-- Render function equivalent -->
<script>
export default {
  render() {
    return h(MyComponent, null, {
      header: () => h('h1', 'Title'),
      default: () => h('p', 'Default content')
    })
  }
}
</script>

Forwarding Slots

// ✅ Forward slots from parent
export default {
  setup(props, { slots }) {
    return () => h(ChildComponent, null, {
      // Forward each slot as a function
      default: slots.default,
      header: slots.header ? () => slots.header() : undefined
    })
  }
}

Quick Checklist

  • Slots in render functions must be functions
  • Function should return VNode(s) or string
  • Named slots: { slotName: () => content }
  • Scoped slots receive slot props as argument
  • Use template syntax when possible for simpler code
  • Forward slots using the slots object functions