Pass all props to children in Vue

While brainstorming some new Vue components, I wanted to have a way that a single wrapper component could fetch all of the data needed for the children views, and pass that data through (around 5 layers of) children down to the the bottom.

So I did some experimenting, and here’s a way to pass all component props down through child components. Link to codepen.

Vue actually makes this very easy for us. All component props are available in the $props object (this.$props if you're not in the template), and we can use the Vue directive v-bind without specifying a specific prop name in order to bind a whole object of props to the child.

Example

Let's assume we have some wrapper component that is going to render a child comp1 component and pass it both propForComp1 and propForComp2 as props.

Here's Comp1.vue:

<template>
  <div class="comp1">
    <span>{{ propForComp1 }}</span>
    <comp2 v-bind="$props" />
  </div>
</template>

<script>
  export default {
    components: Comp2,
    props: [
      'propForComp1',
      'propForComp2'
    ],
  }
</script>

Notice that comp1 is only using propForComp1 and doesn't really care about propForComp2. But since it needs to be included in the props passed to comp2, propForComp2 still needs to be declared inside the props object. If not, $props will not contain it.

You can do this for many levels of components, but remember that the props any child needs must be declared in all parents. So if you have 5 layers, propForComp5 must be declared as a prop in prop1, prop2, prop3, prop4, and prop5. You can make this a little easier by using a Mixin, which is the route I took in the codepen.

UPDATE: You actually don't have to do this last bit! Just like Vue gives up $props, the $attrs object includes all of the passed attributes that the component doesn't declare as props. This means, that we can simply use v-bind="$attrs" to pass down the attributes that children would care about, even if the component doesn't specify them itself.

The previous example would turn into:

<template>
  <div class="comp1">
    <span>{{ propForComp1 }}</span>
    <comp2 v-bind="$props" v-bind="$attrs" />
  </div>
</template>

<script>
  export default {
    components: Comp2,
    props: [
      'propForComp1'
    ],
  }
</script>

A diff to see the changes:

 <template>
   <div class="comp1">
     <span>{{ propForComp1 }}</span>
-    <comp2 v-bind="$props" />
+    <comp2 v-bind="$props" v-bind="$attrs" />
  </div>
 </template>

 <script>
   export default {
     components: Comp2,
     props: [
       'propForComp1',
-      'propForComp2'
     ],
   }
 </script>

Thanks to the DEV Community user "morficus" for pointing this out!