Vue Fallthrough Attributes

A component can be called with attributes that are not declared as props, and they will simply fall through to the root element in the component.

With fallthrough attributes you get a better overview from the parent where the component is created, and it simplifies our code because we don't need to declare the attribute as a prop.

Typical attributes used to fall through are class, style and v-on.

Fallthrough Attributes

It can be nice to for example control the component styling from the parent rather than having the styling hidden away inside the component.

Let's create a new example, a basic todo list in Vue, and see how the style attribute falls through to the components representing things to do.

So, our App.vue should contain the list of things to do, and an <input> element and a <button> to add new things to do. Each list item is a <todo-item /> component.

App.vue:

<template>
  <h3>Todo List</h3>  
  <ul>
    <todo-item
      v-for="x in items"
      :key="x"
      :item-name="x"
    />
  </ul>
  <input v-model="newItem">
  <button @click="addItem">Add</button>
</template>

<script>
  export default {
    data() {
      return {
        newItem: '',
        items: ['Buy apples','Make pizza','Mow the lawn']
      };
    },
    methods: {
      addItem() {
        this.items.push(this.newItem),
        this.newItem = '';
      }
    }
  }
</script>

And TodoItem.vue just receives the description of what to do as a prop:

TodoItem.vue:

<template>
  <li>{{ itemName }}</li>
</template>

<script>
  export default {
    props: ['itemName']
  }
</script>

To build our application correctly we also need the right setup in main.js:

main.js:

import { createApp } from 'vue'
  
import App from './App.vue'
import TodoItem from './components/TodoItem.vue'

const app = createApp(App)
app.component('todo-item', TodoItem)
app.mount('#app')

To see the point of this section, that properties can fall through to the root element inside the <template> of our component, we can give the list items some styling from App.vue:

Example

We give styling to the <li> elements inside the component, from App.vue:

<template>
  <h3>Todo List</h3>
  <ul>
    <todo-item
      v-for="x in items"
      :key="x"
      :item-name="x"
      style="background-color: lightgreen;"
    />
  </ul>
  <input v-model="newItem">
  <button @click="addItem">Add</button>
</template>
Run Example »

To confirm that the style attribute has actually fallen through we can right click an <li> element in our todo list in the browser, choose 'Inspect', and we can see the style attribute is now on the <li> element:


Merging 'class' and 'style' Attributes

If 'class' or 'style' attributes are already set, and 'class' or 'style' attributes also comes from the parent as fallthrough attributes, the attributes will be merged.

Example

In addition to the existing styling from the parent, we add a margin to the <li> elements inside the TodoItem.vue component:

<template>
  <li style="margin: 5px 0;">{{ itemName }}</li>
</template>

<script>
  export default {
    props: ['itemName']
  }
</script>
Run Example »

If we right click an <li> element in the browser we can see that the attributes have been merged. Margin is set directly on the <li> element inside the component, and is merged with the background-color that falls through from the parent:


$attrs

If we have more than one element on the root level of the component, it is no longer clear which element the attributes should fall through to.

To define which root element gets the fallthrough attributes we can mark the element with the built-in $attrs object, like this:

Example

TodoItem.vue:

<template>
  <div class="pinkBall"></div>
  <li v-bind="$attrs">{{ itemName }}</li>
  <div class="pinkBall"></div>
</template>
Run Example »

Vue Exercises

Test Yourself With Exercises

Exercise:

Set the root element of the 'fish-type' component to belong to the 'blue' CSS class (create a fallthrough attribute).

<fish-type  />

Start the Exercise



Copyright 1999-2023 by Refsnes Data. All Rights Reserved.