Explain the difference between native DOM events (click/input/submit...) and component events emitted with $emit/emit in Vue. Cover what happens when you write @click on a real element vs on a component tag, how propagation differs (DOM bubbling vs parent-only emits), and the Vue 2 .native vs Vue 3 fallthrough/emits behavior. Explicit emits improve clarity; test propagation and accessibility of custom components.
Use this Vue interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
What is the difference between native and component events in Vue?Frontend interview answer
This Vue interview question tests whether you can explain Native and Component Events Differ in Vue, connect it to production trade-offs, and handle common follow-up questions.
- Native and Component Events Differ in Vue explanation without falling back to memorized docs wording
- Events and Components reasoning, edge cases, and production failure modes
- How you would answer the most likely Vue interview follow-up
Core idea
Native events are browser DOM events fired by real elements (<button>, <input>, ...).
Component events are custom events emitted by a Vue component (emit('eventName', payload)) and received by the parent.
Aspect | Native (DOM) event | Component event (emit) |
|---|---|---|
Where it originates | Browser fires it on a real DOM node | Component code emits it explicitly |
How you listen | On an element: | On a component tag: |
Propagation | Bubbles/captures through the DOM tree | Goes to the direct parent only (no DOM bubbling) |
Payload | Usually a real | Whatever you emit (could be an Event, but often custom data) |
Default behavior | May have defaults (submit navigates, link navigates, etc.) | No browser default; it’s app-level messaging |
Example: native DOM event
<!-- Parent.vue -->
<script setup>
function onNativeClick(e) {
console.log('native click', e.type);
}
</script>
<template>
<button type="button" @click="onNativeClick">Native click</button>
</template>
Example: component event (custom)
The parent listens on the component tag, but it only fires if the child emits it.
<!-- MyButton.vue -->
<script setup>
const emit = defineEmits(['press']);
function handleClick(e) {
emit('press', { originalEvent: e, ts: Date.now() });
}
</script>
<template>
<button type="button" @click="handleClick">
<slot>Press</slot>
</button>
</template>
<!-- Parent.vue -->
<script setup>
function onPress(payload) {
console.log('component event press', payload.ts);
}
</script>
<template>
<MyButton @press="onPress">Component press</MyButton>
</template>
The confusing part: @click on a component
When you write <MyButton @click=... />, it does not automatically mean “listen to the internal DOM click”. It depends on Vue version and how the component is authored.
Case | What @click means | How to make it work intentionally |
|---|---|---|
Vue 2: | Listens for a component-emitted | Emit it from the child: |
Vue 2: | Attach a native listener to the component’s root DOM element | Use |
Vue 3: listener fallthrough (single-root component) | If the event name is not declared in | Rely on fallthrough only when you really want “wrapper behaves like a DOM element”; otherwise use explicit custom events |
Vue 3: declared in |
| Declare + emit: |
Gotcha | Why it happens | Rule of thumb |
|---|---|---|
Assuming DOM bubbling for component events | Custom emits don’t bubble through DOM; only parent receives it | If grandparents need it, re-emit upward or use a store |
Using DOM event modifiers on custom events (.stop/.prevent) | Those call | Use modifiers mainly for DOM events; for custom events, design payloads and logic explicitly |
Naming a component event the same as a native event ('click') | Can be valid but easily confuses “is this DOM or emitted?” (plus fallthrough rules in Vue 3) | Prefer semantic names: |
Not declaring | Vue can’t validate emitted events and listeners may fall through to DOM unintentionally | Declare |
Interview-ready takeaway
Native event = browser event on a real element (bubbles in DOM).
Component event = explicit emit from child to parent (no DOM bubbling).
On a component tag, @event primarily means “listen for an emitted event”, except Vue 3 can also treat undeclared listeners as fallthrough attributes to the root element.
Practical scenario
A custom BaseButton should emit click to parent components while still handling native DOM events internally.
Common pitfalls
- Using native events on component tags without emitting.
- Relying on Vue 2
.nativebehavior in Vue 3. - Not declaring
emits, which hides event contracts.
Explicit emits add clarity but more boilerplate. Test both DOM and emitted events with unit tests.
Use this as one explanation rep, then continue with the Vue.js interview questions cluster or a guided prep path.