Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

CamKem's avatar
Level 10

Vue defineEmits (upstream emitting)

Hello, I am trying to work out how to use emits in Vue. I have a component that is nested 2 deep, that is emitting a flash message when a button is clicked.

This is my component (TeamMember.vue) that emits the flash message:

<template>
  <tr class="bg-gray-100 px-12">
    <td class="text-xl font-medium flex items-center gap-x-4 px-6 py-4">
      <img :src="`https://i.pravatar.cc/50?u=${email}`" alt="John Doe" class="w-10 h-10 rounded-xl">
      <span>{{ props.name }}</span>
    </td>

    <td class="text-gray-500 px-6 py-4">
      <span>{{ props.email }}</span>
    </td>

    <td class="px-6 py-4">
      <button @click="update(useFlash('clicked'))"  v-if="props.status === 'Active'" class="bg-green-400 hover:bg-green-500 text-white px-4 py-2 rounded">
          {{ props.status }}

      </button>
      <button v-else class="bg-yellow-400 hover:bg-yellow-500 text-white px-4 py-2 rounded">
          {{ props.status }}
      </button>
    </td>
  </tr>

</template>

<script setup>
import {useFlash, flash} from "@/mixins/flash";

let props = defineProps({
  modelValue: {
    type: Object,
    show: Boolean,
    message: String,
  },
  name: String,
  email: String,
  status: String,
})

let emit = defineEmits(["update:modelValue"]);

function update(flash) {
  emit("update:modelValue", flash);
}
</script>

I have checked & it is working, when I click the button the flash.message value changes to "clicked" and flash.show value to true.

Then the FlashMessage component is the following:

<template>
  <div
      v-if="props.flash.show"
      class="absolute p-4 m-2 rounded-md bkg-green text transition-fade opacity-0"
  >
    {{ props.flash.message }}
  </div>
</template>

<script setup>
import { watch } from "vue";

// define the props
let props = defineProps({
  flash: {
    type: Object,
    show: Boolean,
    message: String,
  },
});

// watch the value of show and if it is true, set it to false after 3 seconds
watch(
    () => props.flash.show,
    (show) => {
      if (show) {
        console.log("show is true");
        setTimeout(() => {
          props.flash.show = false;
        }, 3000);
      }
    }
);
</script>

As I mentioned this is being rended, upstream 2 component, in the root App.vue. This is what my App.vue looks like at the moment.

<script setup>
import TeamView from "@/views/TeamView.vue"
import FlashMessage from "@/components/FlashMessage.vue";
import { flash } from "@/mixins/flash.js";
</script>

<template>
  <div class="grid m-2 p-2">
    <div class="place-self-center">
      <TeamView />
      <FlashMessage v-model="flash" :flash="flash" />
    </div>
  </div>
</template>

This is the mixin where flash & useFlash are being exported/imported from (mixins/flash.js)

import {ref} from "vue";

export let flash = ref({
    show: false,
    message: "",
});

export function useFlash(message) {
    flash.value.show = true;
    flash.value.message = message;
}

The View / Component in the middle is the following (TeamView.vue)

<script setup>
import TeamMember from "@/components/TeamMember.vue";
</script>

<template>
  <main style="width: 768px">
  <header class="flex justify-between">
    <div>
      <button
          class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed"
          :disabled="true">
        Add Member (0 Spots Left)
      </button>
    </div>

    <div class="bg-gray-100 py-2 rounded px-4">
      <h3 class="inline-flex items-center text-3xl relative">
        <svg viewBox="0 0 200 200" width="30" height="30" xmlns="http://www.w3.org/2000/svg">
          <circle cx="100" cy="100" fill="yellow" r="78" stroke="black" stroke-width="3"/>
          <g class="eyes">
            <circle cx="61" cy="82" r="12"/>
            <circle cx="127" cy="82" r="12"/>
          </g>
          <path d="m136.81 116.53c.69 26.17-64.11 42-81.52-.73" style="fill:none; stroke: black; stroke-width: 3;"/>
        </svg>
        <span>Team Smiley</span>
        <div
            class="bg-green-400 w-5 h-5 text-xs text-white rounded-full flex justify-center items-center absolute -right-5 -top-3">
          1
        </div>
      </h3>
    </div>
  </header>

  <div class="place-self-center flex flex-col gap-y-3 mt-4">
    <table class="table-fixed border-spacing-2 border-separate">
      <thead>
      <tr>
        <th class="text-left px-6 py-2">Name</th>
        <th class="text-left px-6 py-2">Email</th>
        <th class="text-left px-6 py-2">Status</th>
      </tr>
      </thead>

      <tbody>

      <TeamMember name="John Doe" email="[email protected]" status="Active" />
      <TeamMember name="Jane Doe" email="[email protected]" status="Pending" />
      <TeamMember name="James Doe" email="[email protected]" status="Active" />
      <TeamMember name="Jill Doe" email="[email protected]" status="Pending" />
      <TeamMember name="Jack Doe" email="[email protected]" status="Active" />
      </tbody>
    </table>

    <p class="text-gray-600 italic text-right">There are no remaining team spots, Upgrade to add more.</p>
  </div>

  <footer class="mt-12 bg-gray-100 py-4 text-center">
    <h5 class="font-semibold text-lg">Smiley (5 Member Team) </h5>
    <a href="#" class="text-blue-500 hover:text-blue-600">Terms</a> |
    <a href="#" class="text-blue-500 hover:text-blue-600">Privacy</a> |
    <a href="#" class="text-blue-500 hover:text-blue-600">Contact</a>
  </footer>
  </main>
</template>

How do I make it so that the emitted flash message makes it all the way up to the component where the flash message is being rendered?

Do I need to add anything in the in between component (like the reverse of prop drilling) to send the emitted object upstream?

Thanks in advance!

0 likes
1 reply
CamKem's avatar
CamKem
OP
Best Answer
Level 10

Never mind, I just used Pinia instead & fixed the issue that way, no need to pass the emit upstream.

Please or to participate in this conversation.