Emitting Custom Events
Sending data back up to a parent with defineEmits, completing the one-way data flow that props start.
2 min de lecture
Props send data down, from parent to child. Events are how a child sends information back up, without ever touching the parent's state directly. Together, they form Vue's standard pattern for component communication, often summarized as "props down, events up."
Declaring and emitting an event
<!-- CounterButton.vue -->
<script setup>
const props = defineProps(["count"]);
const emit = defineEmits(["increment"]);
function handleClick() {
emit("increment");
}
</script>
<template>
<button @click="handleClick">Count: {{ count }}</button>
</template><!-- App.vue -->
<script setup>
import { ref } from "vue";
import CounterButton from "./components/CounterButton.vue";
const count = ref(0);
</script>
<template>
<CounterButton :count="count" @increment="count++" />
</template>defineEmits(["increment"]) declares that this component can emit an increment event — documenting its API the same way defineProps does, so anyone reading the component knows what it can communicate outward without having to search the template for every emit() call. emit("increment") fires it; the parent listens with @increment, exactly like listening for a native DOM event.
Notice the child never touches count itself — it only asks the parent to change it. The parent decides how: here, incrementing by one, but it could just as easily be different logic entirely. The child's responsibility ends at "the user clicked; something relevant happened here."
Passing data with an event
Events can carry a payload, passed as additional arguments to emit():
<!-- SearchInput.vue -->
<script setup>
const emit = defineEmits(["search"]);
function handleInput(event) {
emit("search", event.target.value);
}
</script>
<template>
<input @input="handleInput" placeholder="Search..." />
</template><!-- App.vue -->
<script setup>
function handleSearch(query) {
console.log("Searching for:", query);
}
</script>
<template>
<SearchInput @search="handleSearch" />
</template>The value passed to emit("search", event.target.value) arrives as the first argument to whatever handler the parent attaches — handleSearch(query) here.
Building your own v-model with events
Recall from the v-model lesson that it's shorthand for a :value binding plus an event listener. That's not React-style magic specific to native inputs — you can support v-model on your own components by accepting a modelValue prop and emitting an update:modelValue event:
<!-- CustomInput.vue -->
<script setup>
defineProps(["modelValue"]);
defineEmits(["update:modelValue"]);
</script>
<template>
<input :value="modelValue" @input="$emit('update:modelValue', $event.target.value)" />
</template><template>
<CustomInput v-model="username" />
</template>v-model="username" here expands to :model-value="username" @update:model-value="username = $event" — the same pattern as any other prop-plus-event pair, just under names Vue recognizes as its v-model convention.