If you pass an HTML attribute to a component without having a prop defined for it, Vue will either replace it or merge it with existing attributes.
So, let’s say we have this custom input component (let’s name it BaseTextField.vue
):
<template>
<input
type="text"
class="from-base-text-field"
placeholder="BaseTextField placeholder"
/>
</template>
<script>
export default {
// No props defined
}
</script>
And we’re using it from a component called App.vue
:
<template>
<div class="app">
<base-text-field class="from-app" placeholder="App placeholder" />
</div>
</template>
Now if you run this in the browser, you should see that the class of the displayed <input>
element is "from-base-text-field from-app"
, and the placeholder is "App placeholder"
.
So for the class case, the values from App.vue
and BaseTextField
were merged. But for the placeholder, the value passed from App.vue
replaced the value set by BaseTextField
component.
So, when passing attributes that don’t have corresponding props defined, most attributes will be replaced by the passed values except for class
and style
which will be merged instead.
But how to use these attributes somewhere else?
When passing non-prop attributes, the root element of the component will be affected by them. But what if we want to use these attributes on another element?
So, for example, let’s see how we can use these attributes on the input element if we modified the component to something like this:
<template>
<div>
<label>Your Label</label>
<input type="text" class="from-base-text-field" />
</div>
</template>
<script>
export default {
// No props defined
}
</script>
If you run this example, you would notice that the passed placeholder and class are used on the root <div>
element — that’s expected. Now if we want to use them on the <input>
element instead, we should add v-bind="$attrs"
to it, like this:
<input v-bind="$attrs" type="text" class="from-base-text-field" />
Since $attrs
contains all non-prop attributes except class
and style
, you should only see the placeholder
used on the <input>
. The class would still be used on the root element.
If you check the browser now, you would see that the root element is still using the placeholder value. But we should disable this behavior. We can do this by adding inheritAttrs: false
to your component’s options.
So, the final code should look like this:
<template>
<div>
<label>Your Label</label>
<input v-bind="$attrs" type="text" class="from-base-text-field" />
</div>
</template>
<script>
export default {
inheritAttrs: false
}
</script>