The data flow in Vue is unidirectional. What this means is data flows from parent to child components using props. In case, we want to pass information from the child to the parent component we make use of the $emit method.

$emit is a method in Vue used for passing information from child to the parent component. The way it works is $emit method is used to call a custom event from the child component which gets handled by the parent component. You can pass data to this custom event from the child component.

Let’s us understand $emit and custom events with an example. In our example, we will have a list of TV series to which the user can subscribe. By default, the subscribe value will be false.

We will start by creating an application using Vue CLI.

vue create child-parent-component

Once, the installation is complete, we can navigate to the folder and run the server to load the application.

❯ cd child-parent-component
❯ npm run serve

This will run our server on port 8080 http://localhost:8080/

Let’s edit the App.vue file and remove any unwanted code.

// App.vue

<template>
  <TvShow />
</template>

<script>
import TvShow from "./components/TvShow.vue";

export default {
  name: "App",
  components: {
    TvShow,
  },
};
</script>

Here, we we have imported TvShow component but have not created it yet. Let’s go ahead and create one.

❯ touch src/components/TvShow.vue

We will open the file and add some code.

// TvShow.vue

<template>
  <h2>Tv Show</h2>
</template>

<script>
export default {
  name: "TvShow",
};
</script>

pass-data-from-child-to-parent-component-1

Next, we will declare some data in the parent component App.vue which can be passed to the child component TvShow.

data() {
    return {
      series: [
        {
          id: 1,
          name: "Breaking Bad",
          subscribe: false,
        },
        {
          id: 2,
          name: "Squid Game",
          subscribe: false,
        },
        {
          id: 3,
          name: "The Last of us",
          subscribe: false,
        },
        {
          id: 4,
          name: "Dark",
          subscribe: false,
        },
      ],
    };
};

Using v-for we will loop through the series data and pass them using props to the child component TvShow.

<template>
  <div v-for="show in series" :key="show.id">
    <TvShow :id="show.id" :name="show.name" :subscribe="show.subscribe" />
  </div>
</template>

So the entire App.vue file now contain the following code:

<template>
  <div v-for="show in series" :key="show.id">
    <TvShow :id="show.id" :name="show.name" :subscribe="show.subscribe" />
  </div>
</template>

<script>
import TvShow from "./components/TvShow.vue";

export default {
  name: "App",
  components: {
    TvShow,
  },
  data() {
    return {
      series: [
        {
          id: 1,
          name: "Breaking Bad",
          subscribe: false,
        },
        {
          id: 2,
          name: "Squid Game",
          subscribe: false,
        },
        {
          id: 3,
          name: "The Last of us",
          subscribe: false,
        },
        {
          id: 4,
          name: "Dark",
          subscribe: false,
        },
      ],
    };
  },
};
</script>

Let’s make changes in the TvShow.vue file to display the data.

// TvShow.vue

<template>
  <div>
    <h2>{{ name }}</h2>
    <button>{{ subscribe ? "Subcribed" : "Not Subscribed" }}</button>
  </div>
</template>

<script>
export default {
  name: "TvShow",
  props: ["id", "name", "subscribe"],
};
</script>

pass-data-from-child-to-parent-component-1

Whenever, the button is clicked, user can subscribe or unsubscribe to the Tv series. To make this happen, we need to pass this information from the child component TvShow to the parent component App.

For this to work, we will first create a custom event, toggle-subscribe on the button click.

<button @click="buttonClick">
  {{ subscribe ? "Subcribed" : "Not Subscribed" }}
</button>

Next, we will declare the method which emits the custom event toggle-subscribe to the parent component along with the id of the object:

methods: {
  buttonClick() {
    this.$emit("toggle-subscribe", this.id);
  },
}

Here, method $emit will take the name of the custom event toggle-subscribe and the id of the object as the argument which gets passed to the parent component App.

In the App.vue let’s pass the custom event to TvShow component.

// App.vue

<template>
  <div v-for="show in series" :key="show.id">
    <TvShow
      :id="show.id"
      :name="show.name"
      :subscribe="show.subscribe"
      @toggle-subscribe="subscribeTvShow"
    />
  </div>
</template>

Notice we are passing the custom event toggle-subscribe using the v-on shorthand @.

@toggle-subscribe="subscribeTvShow"

So, the entire TvShow file will contain the following code:

<template>
  <div>
    <h2>{{ name }}</h2>
    <button @click="buttonClick">
      {{ subscribe ? "Subcribed" : "Not Subscribed" }}
    </button>
  </div>
</template>

<script>
export default {
  name: "TvShow",
  props: ["id", "name", "subscribe"],
  methods: {
    buttonClick() {
      this.$emit("toggle-subscribe", this.id);
    },
  },
};
</script>

subscribeTvShow is a method in App.vue where we will write the toggle logic.

// App.vue

methods: {
  subscribeTvShow(showId) {
    const show = this.series.find((show) => show.id == showId);
    show.subscribe = !show.subscribe;
  },
}

In this method, we are finding the object via the id property which gets passed in the $emit method from the child component TvShow.

So the entire App.vue now contains the following code:

<template>
  <div v-for="show in series" :key="show.id">
    <TvShow
      :id="show.id"
      :name="show.name"
      :subscribe="show.subscribe"
      @toggle-subscribe="subscribeTvShow"
    />
  </div>
</template>

<script>
import TvShow from "./components/TvShow.vue";

export default {
  name: "App",
  components: {
    TvShow,
  },
  data() {
    return {
      series: [
        {
          id: 1,
          name: "Breaking Bad",
          subscribe: false,
        },
        {
          id: 2,
          name: "Squid Game",
          subscribe: false,
        },
        {
          id: 3,
          name: "The Last of us",
          subscribe: false,
        },
        {
          id: 4,
          name: "Dark",
          subscribe: false,
        },
      ],
    };
  },
  methods: {
    subscribeTvShow(showId) {
      const show = this.series.find((show) => show.id == showId);
      show.subscribe = !show.subscribe;
    },
  },
};
</script>

With this code in place, we can now subscribe or unsubscribe a Tv series from the list using the button.

pass-data-from-child-to-parent-component-1

The above example demonstrates how we can use the $emit and custom events to pass information from the child to the parent component.

Declaring and validating custom events

Similar to props we can also declare and validate the custom events used by the components.

To declare the custom events, we can make of the emit keyword and declare the method names as either array or object.

emits: ["toggle-subscribe"];
export default {
  name: "TvShow",
  props: ["id", "name", "subscribe"],
  emits: ["toggle-subscribe"],
  methods: {
    buttonClick() {
      this.$emit("toggle-subscribe", this.id);
    },
  },
};

You can also declare them as objects:

emits: {
    "toggle-subscribe": (showId) => {
      if (!showId) {
        console.warn("Series id missing!!");
        return false;
      }
      return true;
    },
  }

In this case, we have a validator to check if the showId is passed to the toggle-subscribe method. If not it will throw a warning.

So, the TvShow.vue file now contains the following code:

// TvShow.vue

<template>
  <div>
    <h2>{{ name }}</h2>
    <button @click="buttonClick">
      {{ subscribe ? "Subcribed" : "Not Subscribed" }}
    </button>
  </div>
</template>

<script>
export default {
  name: "TvShow",
  props: ["id", "name", "subscribe"],
  emits: {
    "toggle-subscribe": (showId) => {
      if (!showId) {
        console.warn("Series id missing!!");
        return false;
      }
      return true;
    },
  },
  methods: {
    buttonClick() {
      this.$emit("toggle-subscribe", this.id);
    },
  },
};
</script>

In case, we miss sending the id in the emit method. The validator will complain. For example,

this.$emit("toggle-subscribe");

pass-data-from-child-to-parent-component-4