What Causes This Warning?
This warning occurs when you pass an invalid type to Vue’s h() or createVNode() functions. Valid types include strings (HTML tags), component objects, and certain special types.
The Problem
import { h } from 'vue'
// ❌ Invalid types
h(null) // null is not valid
h(undefined) // undefined is not valid
h(123) // number is not valid
h(true) // boolean is not valid
h({}) // empty object is not a component
The Fix
Use Valid VNode Types
import { h } from 'vue'
// ✅ String (HTML element)
h('div', 'Hello')
h('span', { class: 'text' }, 'Content')
// ✅ Component
import MyComponent from './MyComponent.vue'
h(MyComponent, { prop: 'value' })
// ✅ Fragment
import { Fragment } from 'vue'
h(Fragment, [h('li', 'Item 1'), h('li', 'Item 2')])
// ✅ Teleport
import { Teleport } from 'vue'
h(Teleport, { to: 'body' }, h('div', 'Modal'))
// ✅ Suspense
import { Suspense } from 'vue'
h(Suspense, null, {
default: () => h(AsyncComponent),
fallback: () => h('div', 'Loading...')
})
Common Scenarios
Null Component Check
// ❌ Component might be undefined
const MaybeComponent = someCondition ? MyComponent : undefined
h(MaybeComponent) // Error if undefined!
// ✅ Handle null case
const MaybeComponent = someCondition ? MyComponent : null
if (MaybeComponent) {
return h(MaybeComponent)
}
return h('div', 'Fallback')
// ✅ Or use conditional in render
return someCondition
? h(MyComponent)
: h('div', 'Fallback')
Dynamic Component Resolution
// ❌ Component not found
import { resolveComponent } from 'vue'
const comp = resolveComponent('NonExistentComponent')
h(comp) // Returns undefined if not found!
// ✅ Check before using
const comp = resolveComponent('MyComponent')
if (comp) {
return h(comp, props)
}
return h('div', 'Component not found')
Async Component Loading
import { defineAsyncComponent, h } from 'vue'
// ✅ Define async component properly
const AsyncComp = defineAsyncComponent(() =>
import('./HeavyComponent.vue')
)
// Use in render
return h(AsyncComp, { prop: 'value' })
Computed Component Type
import { computed, h } from 'vue'
import ComponentA from './ComponentA.vue'
import ComponentB from './ComponentB.vue'
export default {
props: ['type'],
setup(props) {
const component = computed(() => {
// ❌ Might return undefined
const map = { a: ComponentA, b: ComponentB }
return map[props.type]
})
return () => {
// ✅ Check for valid component
if (!component.value) {
return h('div', 'Unknown component type')
}
return h(component.value)
}
}
}
Render Function with Slots
export default {
setup(props, { slots }) {
return () => {
// ❌ Slot might be undefined
const content = slots.default()
// ✅ Check slot exists
return h('div', slots.default?.() || 'No content')
}
}
}
List Rendering
import { h } from 'vue'
export default {
props: ['items'],
setup(props) {
return () => {
return h('ul', props.items.map(item => {
// ❌ If item.component is invalid
if (item.component) {
return h(item.component, { key: item.id })
}
// ✅ Provide fallback
return h('li', { key: item.id }, item.text)
}))
}
}
}
Functional Components
// ✅ Valid functional component
const FunctionalComp = (props, { slots }) => {
return h('div', { class: props.class }, slots.default?.())
}
// With proper typing
FunctionalComp.props = ['class']
// Usage
h(FunctionalComp, { class: 'wrapper' }, () => 'Content')
Valid Types Summary
| Type | Example |
|---|---|
| String | h('div'), h('span') |
| Component | h(MyComponent) |
| Fragment | h(Fragment, [...]) |
| Teleport | h(Teleport, { to }) |
| Suspense | h(Suspense, { ... }) |
| Functional | h((props) => h('div')) |
| Async | h(defineAsyncComponent(...)) |
Quick Checklist
- Verify component imports are correct
- Check for undefined/null before using
h() - Use
resolveComponent()with fallback handling - Use
defineAsyncComponent()for async loading - Provide fallbacks for conditional components
- Use Fragment for multiple root elements