Reactive Properties

If the value of a reactive property changes, the component re-renders. When a component re-renders, all the expressions used in the template are re-evaluated.

All public properties are reactive. To mark a property as public, annotate it with @api.

All private properties that contain a primitive value are reactive. If a private property contains an object, to mark it as reactive, annotate it with @track.

A property can have only one decorator.

Tip

Decorators are a JavaScript language feature. The @api and @track decorators are unique to Lightning Web Components.

Public Properties

To expose a public property, decorate it with @api. Public properties define the API for a component. An owner component that uses the component in its markup can access the component’s public properties.

If the value of a public property changes, the component’s template re-renders.

Import the @api decorator from lwc.

import { LightningElement, api } from 'lwc';

Here’s an example of a TodoItem class with an itemName public property. This class is part of a example-todo-item component, where example is the namespace.

// todoItem.js
import { LightningElement, api } from 'lwc';
export default class TodoItem extends LightningElement {
    @api itemName = 'New Item';
}

The component’s template defines a single todo item.

<!-- todoItem.html -->
<template>
    <div>
        <label>{itemName}</label>
    </div>
</template>

A parent component, in this case example-todo-app, can set the itemName property on child example-todo-item components.

Property names in JavaScript are in camel case while HTML attribute names are in kebab case (dash-separated) to match HTML standards. In the example-todo-app template, the item-name attribute on example-todo-item maps to the itemName JavaScript property on example-todo-item.

<!-- todoApp.html -->
<template>
    <div>
        <example-todo-item item-name="Milk"></example-todo-item>
        <example-todo-item item-name="Bread"></example-todo-item>
    </div>
</template>

The parent component can also access and set the itemName property in JavaScript.

// todoApp.js
const myTodo = this.template.querySelector('example-todo-item');
myTodo.itemName // New Item

A component that declares a public property can set only its default value. In our example, the example-todo-item component can’t update the value of the itemName property in the todoItem.js file.

Private Properties

If a property contains a primitive value, the property is reactive. If the property is used in a template, the component re-renders when the property's value changes.

In this example, when the value of firstName or lastName changes, the component re-renders.

If a property contains an object, to track changes on the object's properties, annotate the property with @track. To understand, let's modify our example. Our example initializes firstName and lastName to empty strings, which are primitive values, so the component re-renders when they change.

firstName = '';
lastName = '';

Instead of primitives, let's modify the example to declare a fullName property and initialize it to an object with firstName and lastName properties.

@track
fullName = {
    firstName : '',
    lastName : ''
};

Because the property contains an object, it must be annotated with @track to be reactive. If you remove @track, the component doesn't re-render.

Data Types

There are some limitations on the depth of changes tracked for re-rendering in reactive properties. The tracking depth depends on the type of the reactive property.

Lightning Web Components tracks changes to the internal values of these types of reactive properties:

This behavior is subtle so let’s look at some code. This class has a reactive property, x, which contains an object.

// trackObject.js
import { LightningElement, track } from 'lwc';
export default class TrackObject extends LightningElement {
    @track
    x = {
        a : "",
        b : ""
    };

    init() {
        this.x.a = "a";
        this.x.b = "b";
    }

    update() {
        this.x.a = "aa";
        this.x.b = "bb";
    }
}

The template has a few buttons that change the internal state of x. The onclick handlers for the buttons are wired to the init() and update() methods in the JavaScript file.

<!-- trackObject.html -->
<template>
    <p>object prop: {x.a}</p>
    <p>object prop: {x.b}</p>

    <button onclick={init}>Init</button>
    <button onclick={update}>Update</button>
</template>

When you click either button, the updated values of the member values of x are re-rendered. Changes to x.a and x.b are tracked because x is a plain object.

Now, let’s look at a similar component with a reactive property, x, of type Date.

// trackDate.js
import { LightningElement, track } from 'lwc';
export default class TrackDate extends LightningElement {
    @track x;

    initDate() {
        this.x = new Date();
    }

    updateDate() {
        this.x.setHours(7);
    }
}

Similarly to our previous example, the template has a few buttons that change the internal state of x.

<!-- trackDate.html -->
<template>
    <p>Date: {x}</p>

    <button onclick={initDate}>Init</button>
    <button onclick={updateDate}>Update</button>
</template>

When you click the Init button, the change is tracked and the template is re-rendered. Lightning Web Components can track that x is pointing to a new Date object. However, when you click Update, the template is not re-rendered. Lightning Web Components doesn’t track changes to the value of the Date object.

Note

When you set a reactive property to a value that can’t be tracked, a warning is logged. If you’re trying to debug a scenario where the component isn’t re-rendering on change, look in your browser console For our example, the browser console logs this helpful warning:

Property "x" of [object:vm TrackDate] is set to a non-trackable object,
which means changes into that object cannot be observed.