Vue 3 Component Communication: Parent-Child Data Exchange

Parent-Child Component Communication

Passing Data with Props

In Vue 3, parent components can pass data to child components using props.

Defining Props in Child Components

Child components can declare their expected props using the defineProps function:

// UserProfile.vue
<template>
  <div>{{ userName }}: {{ userAge }}</div>
  <div>{{ userProfile.city }} {{ userProfile.gender }}</div>
  <div v-for="(item, index) in contactList" :key="index">
    {{ item.phone }}
  </div>
</template>

<script lang="ts" setup>
interface Contact {
  phone: string;
}

interface UserProps {
  userName: string;
  userAge: number;
}

const props = defineProps(['userName', 'userAge', 'userProfile', 'contactList']);

// Alternative approach with type definitions
const props = defineProps({
  userName: {
    type: String as PropType<string>,
    default: () => ''
  },
  userAge: {
    type: Number as PropType<number>,
    default: () => 0
  },
  userProfile: {
    type: Object,
    default: () => ({})
  },
  contactList: {
    type: Array as () => Contact[],
    default: () => []
  }
});
</script>

Passing Data from Parent Components

Parent components can pass data to child components by binding props to their template:

// App.vue
<template>
  <UserProfile 
    :userName="userName" 
    :userAge="userAge" 
    :userProfile="userProfile" 
    :contactList="contactList">
  </UserProfile>
</template>

<script lang="ts" setup>
import UserProfile from './components/UserProfile.vue';

const userName = ref('Alex');
const userAge = ref(28);

const userProfile = reactive({
  city: 'New York',
  gender: 'Male'
});

const contactList = ref([
  { phone: '1234567890' },
  { phone: '9876543210' }
]);
</script>

Child-to-Parent Communication with Events

Child components can communicate with parent components by emitting events.

Defining Events in Child Components

Child components can define custom events using defineEmits and trigger them with the emit function:

// UserProfile.vue
<template>
  <div>{{ userName }}: {{ userAge }}</div>
  <div>{{ userProfile.city }} {{ userProfile.gender }}</div>
  <div v-for="(item, index) in contactList" :key="index">
    {{ item.phone }}
  </div>
  <button @click="updateAge">Update Age</button>
  <button @click="updateName">Update Name</button>
</template>

<script lang="ts" setup>
interface Contact {
  phone: string;
}

const props = defineProps(['userName', 'userAge', 'userProfile', 'contactList']);

const emit = defineEmits(['ageUpdated', 'nameChanged']);

const updateAge = () => {
  emit('ageUpdated', 35);
};

const updateName = () => {
  emit('nameChanged', 'Jordan');
};
</script>

Handling Events in Parent Components

Parent components can listen to events emitted by child components and handle them accordingly:

// App.vue
<template>
  <UserProfile 
    :userName="userName" 
    :userAge="userAge" 
    :userProfile="userProfile" 
    :contactList="contactList"
    @age-updated="handleAgeUpdate"
    @name-changed="handleNameChange">
  </UserProfile>
</template>

<script lang="ts" setup>
import UserProfile from './components/UserProfile.vue';

const userName = ref('Alex');
const userAge = ref(28);

const userProfile = reactive({
  city: 'New York',
  gender: 'Male'
});

const contactList = ref([
  { phone: '1234567890' },
  { phone: '9876543210' }
]);

const handleAgeUpdate = (newAge: number) => {
  userAge.value = newAge;
};

const handleNameChange = (newName: string) => {
  userName.value = newName;
};
</script>

Cross-Component Communication with Provide/Inject

For communication between deeply nested components, Vue 3 provides the provide and inject APIs.

Providing Data from Ancestor Components

Ancestor components can provide data to all their descendants using the provide function:

// App.vue
<template>
  <div>
    <UserDashboard />
  </div>
</template>

<script setup lang="ts">
import { ref, provide } from 'vue';
import UserDashboard from './components/UserDashboard.vue';

const contactList = ref([
  { phone: '1234567890' },
  { phone: '9876543210' }
]);

const removeContact = (index: number) => {
  contactList.value.splice(index, 1);
};

const addContact = (contactInfo: { phone: string }) => {
  contactList.value.push(contactInfo);
};

provide('contactData', {
  contacts: contactList,
  removeContact,
  addContact
});
</script>

Injecting Data in Descendant Components

Descendant components can access the provided data using the inject function:

// ContactList.vue
<template>
  <button @click="handleAdd">Add Contact</button>
  
  <div v-for="(contact, index) in contacts" :key='index'>
    <span>{{ contact.phone }}</span>
    <button @click.prevent="handleRemove(index)">
      Remove
    </button>
  </div>
</template>

<script setup lang="ts">
import { inject } from 'vue';

const { contacts, addContact, removeContact } = inject('contactData') || {
  contacts: ref([]),
  addContact: () => {},
  removeContact: () => {}
};

const handleAdd = () => {
  addContact({ phone: '5555555555' });
};

const handleRemove = (index: number) => {
  removeContact(index);
};
</script>

Tags: Vue 3 Component Communication Props events Provide/Inject

Posted on Tue, 15 Sep 2026 16:42:52 +0000 by gdure