Now we want to take a look at how Sibling Vue components can communicate with each other. We have already seen that a Parent can pass a prop to a child and a child can emit an event for communication. What about when a component wants to interact with a sibling component? There are a couple of ways to accomplish this and we’ll look at them now. One approach is to have the parent component act as the relay between two child components. The other option is to set up an event bus which allows direct communication between two child components with no need to involve the parent component.
Sibling Communication Through The Parent
The first method we’ll look at is for sibling to communicate with each other through a common parent component.
Sibling Component Communication Demo
We can start with a demonstration of sibling component communication in action. Note that we have an encompassing Parent component and two child components which are siblings. Go ahead and click the buttons on the sibling components to see how they communicate with each other.
Pretty cool right? Here are the Parent and Child Components that power the demo.
ParentCard.vue
<template>
<div>
<div class="card m-2" style="width: 40rem;">
<div class="card-body">
<h4 class="card-title" v-text="thecardtitle"></h4>
<button @click="momSaidChill" class="btn btn-block btn-outline-danger" v-if="stopFighting()">Mom says
stop fighting!
</button>
<div class="d-flex justify-content-center">
<brother-card :messageson="messageson" @brothersaid="messageDaughter($event)"></brother-card>
<sister-card :messagedaughter="messagedaughter" @sistersaid="messageSon($event)"></sister-card>
</div>
</div>
</div>
</div>
</template>
<script>
import BrotherCard from './BrotherCard.vue';
import SisterCard from './SisterCard.vue';
export default {
components: {BrotherCard, SisterCard},
data() {
return {
thecardtitle: 'Parent Component!',
messagedaughter: '',
messageson: ''
}
},
methods: {
messageDaughter(message) {
this.messagedaughter = message;
},
messageSon(message) {
this.messageson = message;
},
stopFighting() {
if (this.messagedaughter && this.messageson) {
return true;
}
return false;
},
momSaidChill() {
this.messagedaughter = '';
this.messageson = '';
}
}
}
</script>
<style scoped>
div.card {
color: #721c24;
border: 4px solid #f5c6cb;
}
</style>
SisterCard.vue
<template>
<div>
<div class="card m-3" style="width: 15rem;">
<div class="card-body">
<h5 class="card-title" v-text="thecardtitle"></h5>
<p class="card-text">I'm a <b>sister</b>.</p>
<button @click="messageBrother" class="btn btn-warning">Message Brother</button>
<div v-if="messagedaughter" class="mt-3 alert alert-secondary" v-html="messagedaughter"></div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['messagedaughter'],
data() {
return {
thecardtitle: 'Child Component!'
}
},
methods: {
messageBrother() {
this.$emit('sistersaid', 'Mom said do your homework!')
}
}
}
</script>
<style scoped>
div.card {
color: #856404;
border: 4px solid #ffeeba;
}
</style>
BrotherCard.vue
<template>
<div>
<div class="card m-3" style="width: 15rem;">
<div class="card-body">
<h5 class="card-title" v-text="thecardtitle"></h5>
<p class="card-text">I'm a <b>brother</b>.</p>
<button @click="messageSister" class="btn btn-primary">Message Sister</button>
<div v-if="messageson" class="mt-3 alert alert-secondary" v-html="messageson"></div>
</div>
</div>
</div>
</template>
<script>
export default {
props: ['messageson'],
data() {
return {
thecardtitle: 'Child Component!'
}
},
methods: {
messageSister() {
this.$emit('brothersaid', 'Mom said do your homework!')
}
}
}
</script>
<style scoped>
div.card {
color: #004085;
border: 4px solid #b8daff;
}
</style>
Sister To Parent To Brother Communication
So let’s see how this works. First we’ll look at how the sister communicates to the brother. This requires the Parent to act as an intermediary between the two sibling components.
1. SisterCard.vue
First off, in the <template> of the SisterCard.vue component, we set up a @click event listener for a messageBrother() function.
<button @click="messageBrother" class="btn btn-warning">Message Brother</button>
2. SisterCard.vue
Next up, the messageBrother() function in the <script> section of SisterCard.vue triggers. In this method, we emit a sistersaid
event and pass a message of ‘Mom said do your homework!’ as the payload.
messageBrother() {
this.$emit('sistersaid', 'Mom said do your homework!')
}
3. ParentCard.vue
Now in the <template>section of ParentCard.vue, we have a custom @sistersaid event listener which triggers a messageSon() function. So you see, the Parent is acting as a kind of relay between the two siblings here.
<sister-card :messagedaughter="messagedaughter" @sistersaid="messageSon($event)"></sister-card>
4. ParentCard.vue
In the <script> section of ParentCard.vue we now trigger the messageSon() function which accepts the payload from the custom event we had triggered just above and sets it to the messageson
property.
messageSon(message) {
this.messageson = message;
}
5. ParentCard.vue
The messageson
property in the <script> of ParentCard.vue was initially an empty string, but now contains the value ‘Mom said do your homework!’.
data() {
return {
thecardtitle: 'Parent Component!',
messagedaughter: '',
messageson: ''
}
}
6. ParentCard.vue
This messageson
property is bound to the <brother-card> (the sibling of <sister-card>) we are rendering in the <template> of ParentCard.vue.
<brother-card :messageson="messageson" @brothersaid="messageDaughter($event)"></brother-card>
7. BrotherCard.vue
In the <script> section of BrotherCard.vue, we are accepting the messageson
property as a prop. We now have access to data that originated in SisterCard.vue and is now inside of BrotherCard.vue.
props: ['messageson']
8. BrotherCard.vue
Lastly, in the <template> section of BrotherCard.vue we can make use of that data. We use a simple v-if to see if messageson
has any useful data and if so, we display that message inside of an alert div. Cool!
<div v-if="messageson" class="mt-3 alert alert-secondary" v-html="messageson"></div>
Brother To Parent To Sister Communication
The process we described above also holds true going in reverse. Again, this requires the Parent to act as an intermediary between the two sibling components.
1. BrotherCard.vue
First off, in the <template> of the BrotherCard.vue component, we set up a @click event listener for a messageSister() function.
<button @click="messageSister" class="btn btn-primary">Message Sister</button>
2. BrotherCard.vue
Next up, the messageSister() function in the <script> section of BrotherCard.vue triggers. In this method, we emit a brothersaid
event and pass a message of ‘Mom said do your homework!’ as the payload.
messageSister() {
this.$emit('brothersaid', 'Mom said do your homework!')
}
3. ParentCard.vue
Now in the <template>section of ParentCard.vue, we have a custom @brothersaid event listener which triggers a messageDaughter() function. Once again, the Parent is acting as a kind of relay between the two siblings here.
<brother-card :messagedaughter="messagedaughter" @brothersaid="messageDaughter($event)"></brother-card>
4. ParentCard.vue
In the <script> section of ParentCard.vue we now trigger the messageDaughter() function which accepts the payload from the custom event we had triggered just above and sets it to the messagedaughter
property.
messageDaughter(message) {
this.messagedaughter = message;
}
5. ParentCard.vue
The messagedaughter
property in the <script> of ParentCard.vue was initially an empty string, but now contains the value ‘Mom said do your homework!’.
data() {
return {
thecardtitle: 'Parent Component!',
messagedaughter: '',
messageson: ''
}
}
6. ParentCard.vue
This messagedaughter
property is bound to the <sister-card> (the sibling of <brother-card>) we are rendering in the <template> of ParentCard.vue.
<sister-card :messagedaughter="messagedaughter" @sistersaid="messageSon($event)"></sister-card>
7. BrotherCard.vue
In the <script> section of BrotherCard.vue, we are accepting the messagedaughter
property as a prop. We now have access to data that originated in BrotherCard.vue and is now inside of SisterCard.vue.
props: ['messagedaughter']
8. BrotherCard.vue
Lastly, in the <template> section of BrotherCard.vue we can make use of that data. We use a simple v-if to see if messagedaughter
has any useful data and if so, we display that message inside of an alert div. Cool!
<div v-if="messagedaughter" class="mt-3 alert alert-secondary" v-html="messagedaughter"></div>
Sibling Communication Via An Event Bus
As the application grows, passing everything through the parent component can become more tricky. Another option is to instead use an Event Bus type architecture for communicating between sibling components. This makes use of a central class or object to pass the data. Angular uses a similar architecture and it is called Services. Let’s see how we might make use of this.
Brother To Sister Communication
We can use this eventBus architecture to remove the Parent from the communication flow and allow sibling to sibling communication.
1. In the main.js file we can start by defining the new event bus class which is a Vue instance.
main.js
import Vue from 'vue'
import App from './App.vue'
export const eventBus = new Vue();
new Vue({
el: '#app',
render: h => h(App)
});
2. Now, we will import this new event bus into the BrotherCard.vue component.
BrotherCard.vue
import {eventBus} from "../main";
3. The eventBus Vue instance will now be the one to emit an event in BrotherCard.vue. Notice this.$emit is now commented out, and eventBus.$emit is responsible. eventBus is just a Vue instance, and eventBus has this $emit method which is why we are able to do it this way. We still trigger the same custom event name and message.
BrotherCard.vue
methods: {
messageSister() {
// this.$emit('brothersaid', 'Mom said do your homework!')
eventBus.$emit('brothersaid', 'Mom said do your homework!')
}
}
4. Import the eventBus into the sibling SisterCard.vue file.
SisterCard.vue
import {eventBus} from "../main";
5. Add the created() life cycle hook to SisterCard.vue. It is in the created() life cycle hook where we set up a listener for the custom event we just fired from the eventBus. This listener will begin and stay running when the SisterCard.vue component is rendered. The code below simply listens for the ‘brothersaid’ custom event, then triggers the shorthand notation callback you see assigning the message that was passed as the payload of the custom event to the ‘frombrother’ data property.
SisterCard.vue
created() {
eventBus.$on('brothersaid', (message) => {
this.frombrother = message;
});
}
6. Add the frombrother
data property to the data() function in SisterCard.vue. We no longer use a prop from the Parent component in these child components. We are now talking directly between sibling components so we need to have a new data property which we will use to drive the template section.
SisterCard.vue
data() {
return {
thecardtitle: 'Child Component!',
frombrother: ''
}
}
7. Now, in the <template> section of SisterCard.vue – we can conditionally display a message from the brother if there is one present.
SisterCard.vue
<div v-if="frombrother" class="mt-3 alert alert-secondary" v-html="frombrother"></div>
Brother To Sister Communication
Let’s set up the same thing in reverse so that the brother can also communicate to the sister.
1. Once again the main.js file has our eventBus definition which is exported as a constant.
main.js
import Vue from 'vue'
import App from './App.vue'
export const eventBus = new Vue();
new Vue({
el: '#app',
render: h => h(App)
});
2. The SisterCard.vue component imports the eventBus like we saw earlier.
SisterCard.vue
import {eventBus} from "../main";
3. Again we emit a custom event using the eventBus itself in SisterCard.vue.
SisterCard.vue
methods: {
messageBrother() {
eventBus.$emit('sistersaid', 'Mom said do your homework!')
}
}
4. The eventBus is imported into the BrotherCard.vue file.
BrotherCard.vue
import {eventBus} from "../main";
5. The listener for the custom event is set up in the created() life cycle hook in BrotherCard.vue.
BrotherCard.vue
created() {
eventBus.$on('sistersaid', (message) => {
this.fromsister = message;
});
}
6. Add the fromsister
data property to the data() function in BrotherCard.vue.
BrotherCard.vue
data() {
return {
thecardtitle: 'Child Component!',
fromsister: ''
}
}
7. Now, in the <template> section of BrotherCard.vue – we can conditionally display a message from the sister if there is one present.
BrotherCard.vue
<div v-if="fromsister" class="mt-3 alert alert-secondary" v-html="fromsister"></div>
The Parent Component Is Now Simplified
In the parent component, all we have to do is render the children now. We no longer have any click handlers or event listeners in the parent, just a simple render of <brother-card></brother-card> and <sister-card></sister-card> like we see here.
<template>
<div>
<div class="card m-2" style="width: 40rem;">
<div class="card-body">
<h4 class="card-title" v-text="thecardtitle"></h4>
<div class="d-flex justify-content-center">
<brother-card></brother-card>
<sister-card></sister-card>
</div>
</div>
</div>
</div>
</template>
<script>
import BrotherCard from './BrotherCard.vue';
import SisterCard from './SisterCard.vue';
export default {
components: {BrotherCard, SisterCard},
data() {
return {
thecardtitle: 'Parent Component!',
}
}
}
</script>
<style scoped>
div.card {
color: #721c24;
border: 4px solid #f5c6cb;
}
</style>
Sibling To Sibling Communication Demo
Let’s check it out! The parent is completely out of the loop now. Those devious little kids!
Vue Sibling Component Communication Summary
In this tutorial we saw a couple of ways to handle communication between sibling components in VueJS. The first option had us communicating through the parent component using props and custom events. This is where the Parent acts as the relay between the two children. The second approach used an event bus where we were able to communicate directly between sibling components leaving Parent component out of the equation. This all comes down to managing state, and as you can see it can start to get a little tricky as your application grows. This is what the great VueX tool was created for. VueX simplifies state management and we’ll have a look at that tool in a future tutorial.