Lightning Web Components use one-way data binding and custom events to connect parent and child components. This guide covers how to pass data, receive updates, and bubble events.
// parentComponent.js
import { LightningElement, track } from 'lwc';
export default class ParentComponent extends LightningElement {
@track accountName = 'Acme Corp';
@track isLoading = false;
handleNameChange(event) {
this.accountName = event.target.value;
}
}
<!-- parentComponent.html -->
<template>
<lightning-input
type="text"
label="Account Name"
value={accountName}
onchange={handleNameChange}>
</lightning-input>
<!-- Pass data to child -->
<c-child-component
account-name={accountName}
is-loading={isLoading}>
</c-child-component>
</template>
// childComponent.js
import { LightningElement, api, track } from 'lwc';
export default class ChildComponent extends LightningElement {
@api accountName; // Receives from parent
@api isLoading;
@track displayName = '';
connectedCallback() {
// Update display when parent data arrives
if (this.accountName) {
this.displayName = this.accountName.toUpperCase();
}
}
// This is called when @api property changes
displayAccountName() {
return `Account: ${this.accountName}`;
}
}
<!-- childComponent.html -->
<template>
<div class="card">
<h3>{displayAccountName()}</h3>
<p>Status: <span if:true={isLoading}>Loading...</span></p>
</div>
</template>
Use template refs to call methods on child components:
// parentComponent.js
import { LightningElement } from 'lwc';
export default class ParentComponent extends LightningElement {
handleRefresh() {
// Get reference to child component
const childComponent = this.template.querySelector('c-child-component');
// Call child method
childComponent.refresh();
}
handleResetForm() {
const childComponent = this.template.querySelector('c-child-component');
childComponent.resetForm();
}
}
<!-- parentComponent.html -->
<template>
<lightning-button
label="Refresh Data"
onclick={handleRefresh}>
</lightning-button>
<lightning-button
label="Reset Form"
onclick={handleResetForm}>
</lightning-button>
<c-child-component></c-child-component>
</template>
// childComponent.js
import { LightningElement, track } from 'lwc';
export default class ChildComponent extends LightningElement {
@track data = [];
@track formData = {};
// Method called from parent
refresh() {
this.loadData();
}
// Method called from parent
resetForm() {
this.formData = {};
this.template.querySelector('form').reset();
}
loadData() {
// Simulate data fetch
this.data = [
{ id: 1, name: 'Item 1' },
{ id: 2, name: 'Item 2' }
];
}
}
While data flows down via @api, events bubble up from child to parent:
// childComponent.js
import { LightningElement, api } from 'lwc';
export default class ChildComponent extends LightningElement {
@api accountId;
handleSave() {
// Dispatch custom event to parent
const event = new CustomEvent('accountsaved', {
detail: {
accountId: this.accountId,
timestamp: new Date()
}
});
this.dispatchEvent(event);
}
}
// parentComponent.js
import { LightningElement } from 'lwc';
export default class ParentComponent extends LightningElement {
handleAccountSaved(event) {
const { accountId, timestamp } = event.detail;
console.log(`Account ${accountId} saved at ${timestamp}`);
}
}
<!-- parentComponent.html -->
<template>
<c-child-component
account-id="00100000000001"
onaccountsaved={handleAccountSaved}>
</c-child-component>
</template>
// accountListComponent.js
import { LightningElement, track } from 'lwc';
export default class AccountListComponent extends LightningElement {
@track accounts = [
{ id: '001', name: 'Acme Corp', revenue: 1000000 },
{ id: '002', name: 'Global Tech', revenue: 500000 }
];
@track selectedAccountId;
handleAccountSelect(event) {
this.selectedAccountId = event.detail;
}
handleAccountUpdated(event) {
const { accountId, name, revenue } = event.detail;
// Update account in list
const index = this.accounts.findIndex(acc => acc.id === accountId);
if (index !== -1) {
this.accounts[index] = { id: accountId, name, revenue };
}
}
}
<!-- accountListComponent.html -->
<template>
<div class="container">
<h2>Accounts</h2>
<c-account-list-view
accounts={accounts}
onaccountselect={handleAccountSelect}>
</c-account-list-view>
<c-account-editor
if:true={selectedAccountId}
account-id={selectedAccountId}
onaccountupdated={handleAccountUpdated}>
</c-account-editor>
</div>
</template>
@api properties to receive data in child componentstemplate.querySelector() to call child methodsCustomEvent and dispatchEvent()