Bind parent-scope attributes to a sub node in Vue

If you work with Vue.js, you probably already created (or will) your own components within your application. By component, I mean a small piece of user interface such as a button, a text input or a carousel.

I recently struggled while I was working with a custom input component. The problem was that parent-scope attributes I was adding to the component were not bound to my input. Then I learned these two Vue features:

Context

This is a simplified version of the component:

<template>
  <div class="InputComponent">
    <input v-model="model" type="text" />
  </div>
</template>

<script>
export default {
  name: 'InputComponent',
  data() {
    return {
      model: ''
    }
  }
}
</script>

Let's say I want to import this component and add some attributes to it:

<input-component data-tracking="name" id="name"></input-component>

By doing this, if I inspect my HTML with the devtools, I can see that the input tag didn't receive the id nor the data-tracking attributes. But the main wrapper of the component (.InputComponent) did.

Vue, $attrs and inheritAttrs

The above behavior is the default one: Vue creates an object with the attributes (only those not linked to a prop) and then spreads it to the root element of a component.

You can choose to remove this default behavior by adding the property inhertiAttrs: false to the component. Doing this will give you access to a new object with all parent-scope attributes: $attrs.

Then you can use the v-bind directive to spread this object on the desired element. Here is the updated input component:

<template>
  <div class="InputComponent">
    <input v-model="model" type="text" v-bind="$attrs" />
  </div>
</template>

<script>
export default {
  name: 'InputComponent',
  inheritAttrs: false,
  data() {
    return {
      model: ''
    }
  }
}
</script>

And here is the rendered component:

<div class="InputComponent">
  <input type="text" data-tracking="name" id="name" />
</div>

Wrapping up

That's it. I love the flexibility Vue gives to developers. Even after almost 2 years working with this framework, I'm still learning this kind of cool features.