Using @tracked for Local Component State in Modern Ember Octane
Declare mutable local state with @tracked class fields in Ember Octane, avoid in‑place mutations, and verify updates with ember serve or a rendering test.
31 May 2026, 07:28 UTC

Useful answer
In modern Ember (Octane and later) you should declare mutable local component state with the @tracked decorator on a class field, and use plain getters or computed properties only for values derived from that state.
Mechanism
The @tracked decorator marks a property as reactive. When a template or a getter reads the field, Ember records the dependency. Assigning a new value changes the reference (or the property) and automatically invalidates any dependent computation, causing a rerender of the component.
Worked example
Generate a component:
ember generate component counter
Edit app/components/counter.js:
import { tracked } from '@ember-decorators';
import Component from '@glimmer/component';
export default class Counter extends Component {
@tracked count = 0;
get doubled() {
return this.count * 2;
}
increment() {
this.count += 1;
}
}
The template (counter.hbs):
{{this.doubled}}
Add
Clicking Add updates count, recomputes doubled, and the UI reflects the new value.
Limits and common mistakes
- Instance‑local:
@trackedonly affects the component that owns the field; it cannot share state between routes or unrelated components. - Shallow observation: it does not deeply watch nested objects. Mutating a property inside a nested object without reassigning the outer reference will not trigger an update.
- Do not place
@trackedon a getter or method; use plain getters for derived values and@computedonly when you need its specific caching behavior. - In‑place array or object mutation (e.g.,
this.items.push(x)) will not re‑render the UI unless you replace the reference (e.g.,this.items = [...this.items, x]). - Using plain object property changes without a tracked reference will be ignored by Ember’s reactivity system.
Version‑sensitive behavior
@tracked was introduced with Ember Octane; earlier versions still support @computed but it is discouraged for new local state. Verify that your project’s Babel and Ember build configuration enable class fields and the decorator syntax for your Ember version.
Verification steps
- Create the component:
ember generate component counter(requires developer‑level access to the Ember CLI). - Open
app/components/counter.jsand add the@trackedfield and methods shown above. - Run the dev server:
ember serve. Visit the route rendering the component and click theAddbutton; the number should increment. - Write a rendering test:
await this.render(Counter); await this.click('button'); assert.dom('.counter-value').hasText('2');(adjust selectors as needed). - Check
package.jsonfor the Ember version and confirm that@trackedand native class fields are supported.
Debugging tips
If the UI does not update, ensure the @tracked field is a class property, not a local variable, and that you mutate the field directly rather than creating a new object without reassigning the reference. Use the Ember Inspector to view reactive dependencies and confirm that the component is re‑rendering.
Common symptoms include the value staying constant after a click or the component re‑rendering without the displayed number changing, usually pointing to an in‑place mutation or a missing @tracked decorator.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.