What is the difference between native and component events in Vue?

HighIntermediateVue
Preparing for interviews?

Use guided tracks for structured prep, then practice company-specific question sets when you want targeted interview coverage.

Quick Answer

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.

Answer

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: <button @click=...>

On a component tag: <MyComp @save=...>

Propagation

Bubbles/captures through the DOM tree

Goes to the direct parent only (no DOM bubbling)

Payload

Usually a real Event object

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

Native DOM events vs component-emitted events

Example: native DOM event

HTML
<!-- 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.

HTML
<!-- 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: @click on component

Listens for a component-emitted click event (not DOM)

Emit it from the child: this.$emit('click', $event)

Vue 2: @click.native

Attach a native listener to the component’s root DOM element

Use .native (Vue 2 only)

Vue 3: listener fallthrough (single-root component)

If the event name is not declared in emits, Vue treats it as an attribute listener and can pass it to the root element

Rely on fallthrough only when you really want “wrapper behaves like a DOM element”; otherwise use explicit custom events

Vue 3: declared in emits

@click becomes a component event listener (must be emitted)

Declare + emit: defineEmits(['click']) and emit('click', $event)

Vue 2 vs Vue 3 behavior on component listeners

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 event.stopPropagation()/preventDefault() — only meaningful if your payload is a real Event

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: press, submit, close, update:modelValue

Not declaring emits in Vue 3

Vue can’t validate emitted events and listeners may fall through to DOM unintentionally

Declare emits for your component API; it prevents accidental fallthrough for those event names

Common interview-level pitfalls

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 .native behavior in Vue 3.
      • Not declaring emits, which hides event contracts.
Trade-off or test tip
Explicit emits add clarity but more boilerplate. Test both DOM and emitted events with unit tests.

Similar questions
Guides
3 / 34