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!