LWC ships three decorators from the lwc module: @api, @track, and @wire. Each one tells the framework to treat a class member specially.
@api — make a property or method public
@api declares a property or method as part of the component’s public surface. Parents can read, write, or call it; everything else stays private to the component.
import { LightningElement, api } from 'lwc';
export default class ContactCard extends LightningElement {
@api recordId; // parent sets <c-contact-card record-id="003..."></c-contact-card>
@api variant = 'base'; // default value
@api refresh() { // public method — parent can call this.template.querySelector('c-contact-card').refresh()
// ...
}
}
Two rules that trip people up:
- Property names are kebab-cased in HTML, camelCased in JS.
recordId→record-id. - Public properties are reactive by default. When the parent changes the value, the child re-renders.
@track — watch deep mutations on objects and arrays
Since LWC API version 49 (Winter ‘21), primitive fields are reactive without @track. Reassign a string or number and the template re-renders. But the framework only observes shallow changes on objects and arrays — assigning a new value to the field, not mutating its internals.
import { LightningElement, track } from 'lwc';
export default class TodoList extends LightningElement {
items = ['buy milk']; // reactive on reassignment only
@track filters = { // reactive on deep mutation too
showCompleted: false,
priority: 'high'
};
addItem(name) {
this.items = [...this.items, name]; // reassignment — works without @track
}
togglePriority() {
this.filters.priority = 'low'; // deep mutation — needs @track
}
}
The modern convention is to prefer immutable updates (spread, map, filter) over @track. You rarely need the decorator in new code.
@wire — subscribe to a reactive data adapter
@wire connects a property or method to a wire adapter — a function that pushes a stream of { data, error } values. The framework manages caching, refresh, and reactive parameters for you.
import { LightningElement, wire, api } from 'lwc';
import { getRecord } from 'lightning/uiRecordApi';
const FIELDS = ['Contact.Name', 'Contact.Email'];
export default class ContactCard extends LightningElement {
@api recordId;
@wire(getRecord, { recordId: '$recordId', fields: FIELDS })
contact; // contact.data | contact.error
get name() { return this.contact?.data?.fields?.Name?.value; }
get email() { return this.contact?.data?.fields?.Email?.value; }
}
The $ prefix on $recordId marks the parameter as reactive — change recordId and the wire re-runs.
Quick comparison
| Decorator | Purpose | Reactive? |
|---|---|---|
@api | Expose property/method to parent | Yes — parent updates trigger re-render |
@track | Observe deep object/array mutations | Yes — required only for deep changes |
@wire | Subscribe to a data adapter | Yes — reactive parameters trigger re-fetch |
What interviewers are listening for
A common follow-up: “Do I always need @track for arrays?” The honest answer is no — and saying so demonstrates you’ve kept up with API version changes since Winter ‘21. Mention that the idiomatic LWC pattern is immutable updates, and you’ve separated yourself from candidates copy-pasting decorators from older tutorials.
Verified against: LWC Developer Guide — Reactivity, Decorators reference. Last reviewed 2026-05-17 for Spring ‘26 release.