What Causes This Warning?
This warning occurs when you use v-on without specifying an event name (using the object syntax) but pass a non-object value.
The Problem
<!-- ❌ v-on with no argument but not an object -->
<template>
<button v-on="handleClick">Click</button>
<button v-on="'click'">Click</button>
<button v-on="123">Click</button>
</template>
The Fix
Use Object Syntax
<template>
<!-- ✅ Object with event: handler pairs -->
<button v-on="{ click: handleClick }">Click</button>
<!-- ✅ Multiple events -->
<button v-on="{ click: handleClick, mouseenter: handleHover }">
Hover or Click
</button>
</template>
<script setup>
function handleClick() {
console.log('Clicked!')
}
function handleHover() {
console.log('Hovered!')
}
</script>
Use Specific Event Binding
<template>
<!-- ✅ Standard event binding -->
<button @click="handleClick">Click</button>
<button v-on:click="handleClick">Click</button>
</template>
Common Scenarios
Dynamic Event Binding
<script setup>
import { computed } from 'vue'
const events = computed(() => ({
click: handleClick,
mouseenter: handleMouseEnter,
mouseleave: handleMouseLeave
}))
</script>
<template>
<!-- ✅ Bind multiple events dynamically -->
<div v-on="events">
Interactive element
</div>
</template>
Passing Events to Child Components
<script setup>
// Parent component
const buttonEvents = {
click: () => console.log('clicked'),
focus: () => console.log('focused'),
blur: () => console.log('blurred')
}
</script>
<template>
<!-- ✅ Pass event object to child -->
<CustomButton v-on="buttonEvents" />
</template>
Forwarding All Listeners
<!-- Wrapper component -->
<script setup>
import { useAttrs } from 'vue'
const attrs = useAttrs()
// In Vue 3, listeners are part of attrs
const listeners = computed(() => {
const result = {}
for (const key in attrs) {
if (key.startsWith('on') && typeof attrs[key] === 'function') {
result[key] = attrs[key]
}
}
return result
})
</script>
<template>
<div class="wrapper">
<!-- Forward all event listeners -->
<InnerComponent v-bind="listeners" />
</div>
</template>
Conditional Events
<script setup>
import { computed, ref } from 'vue'
const isEnabled = ref(true)
const conditionalEvents = computed(() => {
if (!isEnabled.value) return {}
return {
click: handleClick,
keydown: handleKeydown
}
})
</script>
<template>
<button v-on="conditionalEvents">
{{ isEnabled ? 'Active' : 'Disabled' }}
</button>
</template>
Combining Static and Dynamic Events
<template>
<!-- ✅ Mix static and dynamic events -->
<button
@click="handleClick"
v-on="additionalEvents"
>
Button
</button>
</template>
<script setup>
const additionalEvents = {
mouseenter: () => console.log('enter'),
mouseleave: () => console.log('leave')
}
</script>
With Event Modifiers
<template>
<!-- When using object syntax, add modifiers differently -->
<form v-on="formEvents">
<button type="submit">Submit</button>
</form>
</template>
<script setup>
const formEvents = {
submit: (e) => {
e.preventDefault() // Manual modifier
handleSubmit()
}
}
</script>
Options API
export default {
computed: {
eventHandlers() {
return {
click: this.handleClick,
mouseenter: this.handleMouseEnter
}
}
},
methods: {
handleClick() { /* ... */ },
handleMouseEnter() { /* ... */ }
}
}
<template>
<div v-on="eventHandlers">Content</div>
</template>
Quick Checklist
-
v-onwithout argument requires an object:v-on="{ event: handler }" - For single events, use
@event="handler"orv-on:event="handler" - Object keys are event names, values are handler functions
- Use computed for dynamic event objects
- Modifiers must be handled manually in object syntax