Component Communication
Two-Way Binding Between Components
Two-way binding between components keeps a value synchronized between a parent and child component, allowing both to participate in updating it.
Example
Updating the number of items in a cart from a reusable selector.
VuedefineModel + v-model
<script setup lang="ts">
import { ref } from 'vue'
import QuantitySelector from './QuantitySelector.vue'
const quantity = ref(1)
</script>
<template>
<p>Items in cart: {{ quantity }}</p>
<QuantitySelector v-model="quantity" />
</template>
// QuantitySelector.vue
<script setup lang="ts">
const quantity = defineModel<number>({ required: true })
function increment() {
quantity.value += 1
}
</script>
<template>
<button @click="increment">Add one</button>
</template>
The child declares defineModel(), which creates a model prop it can update. The parent binds it with v-model.
Reactvalue + onChange
import { useState } from 'react'
import { QuantitySelector } from './QuantitySelector'
function App() {
const [quantity, setQuantity] = useState(1)
return (
<>
<p>Items in cart: {quantity}</p>
<QuantitySelector value={quantity} onChange={setQuantity} />
</>
)
}
// QuantitySelector.tsx
type QuantitySelectorProps = {
value: number
onChange: (value: number) => void
}
export function QuantitySelector({
value,
onChange,
}: QuantitySelectorProps) {
return (
<button onClick={() => onChange(value + 1)}>Add one</button>
)
}
The parent keeps the state and passes the value along with a function for the child to request changes.
Angularmodel() + [(...)]
import { Component, signal } from '@angular/core'
import { QuantitySelectorComponent } from './quantity-selector.component'
@Component({
selector: 'app-root',
standalone: true,
imports: [QuantitySelectorComponent],
template: `
<p>Items in cart: {{ quantity() }}</p>
<app-quantity-selector [(quantity)]="quantity" />
`,
})
export class AppComponent {
quantity = signal(1)
}
// quantity-selector.component.ts
import { Component, model } from '@angular/core'
@Component({
selector: 'app-quantity-selector',
standalone: true,
template: `
<button (click)="increment()">Add one</button>
`,
})
export class QuantitySelectorComponent {
quantity = model.required<number>()
increment() {
this.quantity.update((value) => value + 1)
}
}
The child declares model() and the parent creates a two-way binding with [(quantity)].
