Using @tracked in Ember Octane: Fine‑Grained UI Reactivity Made Simple
Discover how Ember Octane’s @tracked decorator and Glimmer components replace classic computed properties, enabling precise UI updates. A step‑by‑step example shows state changes, shallow tracking limits, and how to avoid common pitfalls.
14 Nov 2025, 15:57 UTC

Problem: Unpredictable UI Updates in Classic Ember
In Ember 2.x and earlier, UI state was usually expressed with computed properties and observer callbacks. Both approaches required explicit dependency lists or manual invalidation. When multiple parts of an application accessed the same underlying data, developers often ended up with duplicated logic, stale values, or unintuitive update cycles. The result was a fragile UI that could break when a new property was added or a mutation was performed in place.
Thesis: Autotracking with @tracked and Glimmer Components
Ember Octane (3.15+) introduces Glimmer components that use native ES classes and the @tracked decorator. When a component renders, Ember records which tracked properties are read. On subsequent renders, only those properties that changed will trigger a re‑run. This eliminates the need for manual dependency declarations, making UI updates predictable and easier to reason about.
Section 1: The Octane Autotracking Mechanism
- Tracking Scope: Only properties marked with
@trackedare considered. - Reference Tracking: Reassignment of a tracked property (e.g.,
this.count = this.count + 1) signals a change. Mutating the internal state of an object without reassignment does not trigger a render. - Template Co‑location: Component JavaScript and Handlebars templates live in the same folder, improving discoverability and keeping logic close to markup.
Section 2: Concrete Example – A Filterable List
Below is a minimal component that demonstrates how @tracked drives UI updates. The component maintains a filter string and a list of items. When the filter changes, only the filtered list re‑renders.
// app/components/filterable-list.js
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
export default class FilterableListComponent extends Component {
@tracked filter = '';
get filteredItems() {
// computed on the fly – no decorator needed because it isn’t tracked
return this.args.items.filter(item =>
item.toLowerCase().includes(this.filter.toLowerCase())
);
}
updateFilter(event) {
// Reassignment triggers autotracking
this.filter = event.target.value;
}
}
{{!-- app/components/filterable-list.hbs --}}
<input type="text" placeholder="Filter" oninput={{this.updateFilter}} />
{{#each this.filteredItems as |item|}}
<li>{{item}}</li>
{{/each}}
Running ember serve and typing into the input will instantly update the list without any manual dependency wiring.
Section 3: Shallow Tracking – A Common Pitfall
Because @tracked only observes reference changes, mutating an object or array in place will not trigger a re‑render. For example:
// ❌ This will not update the UI
this.someObject.value = 42;
To force a re‑render, you must reassign the reference:
// ✅ This triggers autotracking
this.someObject = { ...this.someObject, value: 42 };
When dealing with nested state, consider using immutable update patterns or @tracked on the nested property itself.
Section 4: Trade‑Offs and Performance Considerations
- Granularity vs. Overhead: Tracking many small properties can increase render churn. Group related state into a single tracked object when appropriate.
- Migration Cost: Converting a large classic component that relies on observers to Octane requires refactoring and careful testing.
- Deep Mutations: As noted, nested mutations are ignored. This can lead to subtle bugs if developers are used to classic Ember’s deep observation.
Actionable Checklist for Your Octane Project
- Verify Octane Support: Run
ember --versionornpm list ember-sourceto confirm you’re on Ember 3.15+. - Co‑locate Components: Place
.jsand.hbsfiles in the same directory. - Mark State as Tracked: Add
@trackedto any property that the template reads and that you plan to mutate. - Reassign on Change: Mutate tracked objects via reassignment or immutable patterns to trigger updates.
- Run Tests: After refactoring, execute
ember testto catch regressions caused by shallow tracking assumptions.
By following these steps, you’ll harness Ember Octane’s autotracking to create predictable, maintainable UI components without the boilerplate of computed properties or observers.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.