When building Vue 3 components, especially with the <script setup>
syntax, you'll often need to emit custom events. Vue provides two main ways to do this: using $emit
or defineEmits
. While both accomplish the same goal, they serve different use cases and offer varying levels of type safety and clarity.
$emit
Traditionally, $emit
has been used in the Options API and is also available in the Composition API through the setup context:
export default {
setup(props, { emit }) {
emit('custom-event', 'hello')
}
}
In <script setup>
, $emit
can be used directly in templates, but it lacks type checking and auto-completion:
<script setup>
function handleClick() {
// Not type-safe
$emit('custom-event', 'hello')
}
</script>
defineEmits
Vue 3 introduces defineEmits
for <script setup>
, which offers better developer ergonomics and full TypeScript support:
const emit = defineEmits<{
(e: 'custom-event', value: string): void
(e: 'another-event', id: number): void
}>()
emit('custom-event', 'hello')
emit('another-event', 123)
You can also use it with a simple array if you're not using TypeScript:
const emit = defineEmits(['custom-event', 'another-event'])
Key differences
Feature | $emit |
defineEmits |
---|---|---|
Availability | Options API or global in template | <script setup> only |
Type safety | No | Yes |
Auto-completion | No | Yes (with TypeScript) |
Recommended usage | Legacy or quick use | Preferred in <script setup> |
Explicit event list | No | Yes |
Conclusion
If you're using the <script setup>
syntax, defineEmits
is the modern and type-safe way to handle custom events. It makes your components more maintainable and your development experience smoother. Use $emit
only when working within the Options API or when you don't need strong typing.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.