Skip to main content

SF-9007 · Scenario · Medium

How do you pass data from a parent LWC to a child LWC?

✓ Verified by Vikas Singhal · Last reviewed 5/17/2026 · Updated for Spring '26

In LWC, data flows from parent to child via public properties declared with @api. The template binds parent values to child attributes the same way HTML always has.

The basic pattern

Child component declares the public surface:

// childCard.js
import { LightningElement, api } from 'lwc';

export default class ChildCard extends LightningElement {
    @api title;
    @api items = [];      // default value
    @api variant = 'base';
}
<!-- childCard.html -->
<template>
    <div class={variant}>
        <h2>{title}</h2>
        <ul>
            <template for:each={items} for:item="item">
                <li key={item.id}>{item.name}</li>
            </template>
        </ul>
    </div>
</template>

Parent binds values:

<!-- parentList.html -->
<template>
    <c-child-card
        title={cardTitle}
        items={contactList}
        variant="emphasis">
    </c-child-card>
</template>
// parentList.js
import { LightningElement } from 'lwc';

export default class ParentList extends LightningElement {
    cardTitle = 'Active Contacts';
    contactList = [
        { id: '1', name: 'Ada Lovelace' },
        { id: '2', name: 'Grace Hopper' }
    ];
}

Three rules to remember:

  1. kebab-case in HTML, camelCase in JS. A child’s firstName is first-name in markup. Salesforce is consistent — base components like lightning-input follow the same convention.
  2. Attributes are strings; properties are typed. Curly-brace expressions (title={cardTitle}) pass the actual JS value. A literal variant="emphasis" passes the string "emphasis".
  3. Public properties are reactive. When the parent changes cardTitle, the child re-renders automatically — no event needed.

Calling a public method on a child

@api works on methods too. The parent grabs the child via a ref or query and invokes it directly:

// modalChild.js
import { LightningElement, api } from 'lwc';

export default class ModalChild extends LightningElement {
    @api open()  { this.isOpen = true; }
    @api close() { this.isOpen = false; }
    isOpen = false;
}
<template>
    <c-modal-child lwc:ref="modal"></c-modal-child>
    <button onclick={handleOpen}>Open</button>
</template>
handleOpen() {
    this.refs.modal.open();
}

lwc:ref is the modern way to get at child elements — no more this.template.querySelector(...) for components you’ve named yourself.

What you shouldn’t do

Don’t pass mutable state down and expect children to mutate it. LWC enforces a one-way data flow: the child treats @api props as read-only. If a child needs to push a change back, it raises an event (see child-to-parent communication).

// In the child — DON'T do this:
@api items = [];
addItem() {
    this.items.push('new');     // mutates the parent's array. Anti-pattern.
}

Don’t use @api for everything. Internal state should stay private — public surface is API contract you have to maintain. A useful test: if a different team consumed this component, would they need to know about this property? If not, leave it private.

Boolean props and the is-active trap

Boolean @api props receive a subtle gotcha. In HTML, the presence of an attribute means true:

<c-toggle is-active></c-toggle>          <!-- true -->
<c-toggle is-active={state}></c-toggle>  <!-- the JS value of state -->
<c-toggle is-active="false"></c-toggle>  <!-- the STRING "false" — truthy! -->

The third form is the bug. If a value can be dynamic, always bind it with curly braces.

Interviewer-pleasing extras

  • Property changes call a setter, if you define one. Use a set accessor to validate or transform incoming values before storing them.
  • @api properties can’t share a name with reserved attributes like class, is, style, slot, tabindex — the framework will throw at compile time.
  • For complex data structures, prefer passing an object or array reference over many small props. It’s easier to evolve the contract over time.

Verified against: LWC Developer Guide — Communicate Between Components. Last reviewed 2026-05-17 for Spring ‘26 release.