Skip to content

noVueShadowNativeEvents (JavaScript)

Language JavaScript (and super languages)
biome.json
{
"linter": {
"rules": {
"nursery": {
"noVueShadowNativeEvents": "error"
}
}
}
}

Disallow Vue component events named after built-in browser events.

A component can send its own events to its parent with emit. When such an event has the same name as a browser event, like click or keydown, it looks like the browser event but works differently:

  • The listener receives whatever value the component passes to emit, instead of the browser’s Event object.
  • The event only reaches the parent that listens to the component. Most browser events also reach every element that contains the element where they happened.
  • Modifiers that act on the browser’s Event object, such as @click.stop and @click.prevent, do nothing unless the component passes that object along.

Give the event a name that describes what happened in the component, such as save or update:modelValue.

This rule checks the event names listed in the emits option and in defineEmits(). It also checks the event names passed to this.$emit(), to the emit function that setup() receives, to the function returned by defineEmits(), and to $emit() in templates. Uppercase and lowercase letters are treated the same, so Click is reported too.

<script setup>
defineEmits(["click"]);
</script>
code-block.vue:2:14 lint/nursery/noVueShadowNativeEvents ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This component event has the same name as the browser event click.

1 │ <script setup>
> 2 │ defineEmits(["click"]);
│ ^^^^^^^
3 │ </script>
4 │

ℹ It is easy to mistake for the browser event, but it works differently: listeners receive the value passed to emit instead of an Event object, the event doesn't reach the elements around the component, and modifiers such as .stop don't work on it.

ℹ Rename the event to describe what happened in the component.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

<script setup lang="ts">
defineEmits<{ keydown: [key: string] }>();
</script>
code-block.vue:2:15 lint/nursery/noVueShadowNativeEvents ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This component event has the same name as the browser event keydown.

1 │ <script setup lang="ts">
> 2 │ defineEmits<{ keydown: [key: string] }>();
│ ^^^^^^^
3 │ </script>
4 │

ℹ It is easy to mistake for the browser event, but it works differently: listeners receive the value passed to emit instead of an Event object, the event doesn't reach the elements around the component, and modifiers such as .stop don't work on it.

ℹ Rename the event to describe what happened in the component.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

<script>
export default {
methods: {
onClick() {
this.$emit("change", 42);
},
},
};
</script>
code-block.vue:5:18 lint/nursery/noVueShadowNativeEvents ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

⚠ This component event has the same name as the browser event change.

3 │ methods: {
4 │ onClick() {
> 5 │ this.$emit("change", 42);
│ ^^^^^^^^
6 │ },
7 │ },

ℹ It is easy to mistake for the browser event, but it works differently: listeners receive the value passed to emit instead of an Event object, the event doesn't reach the elements around the component, and modifiers such as .stop don't work on it.

ℹ Rename the event to describe what happened in the component.

ℹ This rule belongs to the nursery group, which means it is not yet stable and may change in the future. Visit https://biomejs.dev/linter/#nursery for more information.

<script setup>
const emit = defineEmits(["save"]);
emit("save");
</script>