Vue Communication

Exchange data and actions between parent, child, sibling, and distant components.

Overview

Components rarely work alone. Vue organizes communication with one-way data flow: parents send data down through props, while children report actions upward through custom events. Siblings coordinate through shared parent state, and larger applications can use provide/inject or Pinia for broader dependencies.

Key concepts

  • Props send parent data to children
  • Custom events notify parents
  • Sibling state is usually lifted to a common parent
  • Pinia manages application-wide shared state

Why is Component Communication important?

A food-delivery interface may contain a search box, restaurant list, food cards, and shopping cart. Search text must filter results, food-card actions must add items, and the cart must calculate totals. Communication lets focused components collaborate without merging all responsibilities into one file.

Parent and Child Components

A parent renders one or more children. Most Vue communication follows these direct relationships.

Output
App
 Header
 ProductList
    ProductCard
    ProductCard
    ProductCard
 Footer

Passing Data from Parent to Child

Props are read-only inputs supplied by a parent. The child declares the values it accepts and renders them without taking ownership of the parent's state.

Child: ProductCard.vue

Vue SFC
<script setup>
defineProps({
  productName: {
    type: String,
    required: true
  },
  price: {
    type: Number,
    required: true
  }
});
</script>

<template>
  <article>
    <h2>{{ productName }}</h2>
    <p>{{ price }}</p>
  </article>
</template>

Parent usage

HTML
<ProductCard product-name="Laptop" :price="65000" />

Static string props can omit v-bind. Dynamic values and non-string types use : so Vue evaluates the expression.

Sending Data from Child to Parent

A child emits a custom event to describe what happened. The parent listens and decides how application state should change.

Child emits an event

Vue SFC
<script setup>
const props = defineProps({ product: Object });
const emit = defineEmits(["add-to-cart"]);

function addProduct() {
  emit("add-to-cart", props.product);
}
</script>

<template>
  <button @click="addProduct">Add to Cart</button>
</template>

Parent handles the event

HTML
<ProductCard
  :product="product"
  @add-to-cart="updateCart"
/>

Using kebab-case event names in templates keeps custom events visually consistent with HTML attributes.

Options API Props and Events

JavaScript
export default {
  props: {
    productName: String
  },
  emits: ["add-to-cart"],
  methods: {
    addProduct() {
      this.$emit("add-to-cart");
    }
  }
};

Do not mutate Props

Props belong to the parent. A child should emit an event requesting a change rather than assigning directly to a prop. When editable local state is required, initialize a local ref or use a component v-model contract.

Component v-model

Vue supports two-way component bindings through a modelValue prop and update:modelValue event. defineModel offers a shorter equivalent in supported Vue versions.

Vue SFC
<script setup>
defineProps({ modelValue: String });
const emit = defineEmits(["update:modelValue"]);
</script>

<template>
  <input
    :value="modelValue"
    @input="emit('update:modelValue', $event.target.value)"
  >
</template>
HTML
<NameInput v-model="name" />

Sharing Data between Sibling Components

Sibling components usually do not communicate directly. Lift shared state to their nearest common parent: one child emits a change, the parent updates its state, and props carry the new state to the other child.

Output
SearchBox
     event
Common Parent
     prop
ProductList

Passing Content with Slots

Slots let a parent provide template content to a child's layout. They communicate UI structure rather than ordinary data values.

Vue SFC
<!-- BaseCard.vue -->
<template>
  <article class="card">
    <slot name="title" />
    <slot />
  </article>
</template>
HTML
<BaseCard>
  <template #title><h2>Account</h2></template>
  <p>Profile information</p>
</BaseCard>

Provide and Inject

provide makes a dependency available to descendants, and inject retrieves it without passing the value through every intermediate component. It is useful for tightly related component trees, themes, and plugin-like services.

JavaScript
// Ancestor
provide("theme", "dark");

// Descendant
const theme = inject("theme", "light");

Use Symbol keys in larger applications to avoid naming collisions, and be deliberate about where provided reactive state is mutated.

Using State Management

When many distant components need the same state, repeatedly passing props and events becomes cumbersome. Pinia stores shared state and actions in a central, traceable location. It is covered in its own lesson.

Choosing a Communication method

NeedRecommended tool
Parent sends data to childProps
Child reports action to parentCustom event
Parent supplies child markupSlots
Sibling coordinationLift state to parent
Deep tree dependencyProvide/inject
Application-wide shared statePinia

Real-life example

A ProductCard receives product details as props. Clicking Add to Cart emits the product to ProductList or a page component. That parent updates cart state, which then flows into the Cart component. ProductCard never reaches into the cart directly.

Common beginner mistakes

  • Mutating props inside a child
  • Making siblings depend on each other directly
  • Passing props through many levels when shared state is more suitable
  • Using global state for data needed by only one parent and child
  • Forgetting to declare emitted events
  • Using inconsistent event names
  • Placing business logic in presentation-only components

Best practices

  • Follow props down and events up
  • Validate prop types and required values
  • Declare emitted events
  • Use meaningful event names that describe what occurred
  • Keep state close to the components that need it
  • Lift state only as high as necessary
  • Use Pinia for genuinely shared application state
  • Keep communication contracts small and explicit

Key takeaways

  • Component communication connects focused UI pieces
  • Props carry parent-owned data downward
  • Custom events notify parents upward
  • Siblings coordinate through common state
  • Slots pass template content
  • Provide/inject serves deep descendants
  • Pinia manages broad shared state
  • One-way data flow keeps updates predictable and maintainable
Let's learn with DevBrainBox AI