*ngFor is built to iterate arrays (and other iterables implementing Symbol.iterator) — a plain JavaScript object, like { red: '#ff0000', green: '#00ff00' }, doesn't implement that iteration protocol, so *ngFor can't loop over it directly without some form of conversion first.
1. The problem
colors = { red: '#ff0000', green: '#00ff00', blue: '#0000ff' };
<!-- This does NOT work — colors is a plain object, not an iterable -->
<div *ngFor="let color of colors">{{ color }}</div>
2. The built-in fix: the KeyValue pipe
<div *ngFor="let entry of colors | keyvalue">
{{ entry.key }}: {{ entry.value }}
</div>
Angular's built-in KeyValuePipe converts a plain object into an array of {key, value} pairs at template-render time — no manual conversion code needed in the component class at all, and it works equally on a plain object or a Map.
3. Default sort order — and how to control it
By default, keyvalue sorts entries alphabetically by key — for many real objects (a settings object with a meaningful, deliberate property order) this reorders things unexpectedly compared to how the object was originally defined. Passing a custom compare function preserves — or controls — a different order:
<div *ngFor="let entry of colors | keyvalue: originalOrder">
{{ entry.key }}: {{ entry.value }}
</div>
originalOrder = (): number => 0; // a comparator that always returns 0 disables re-sorting
4. Converting to an array in the component instead
colors = { red: '#ff0000', green: '#00ff00', blue: '#0000ff' };
colorEntries = Object.entries(this.colors).map(([key, value]) => ({ key, value }));
<div *ngFor="let entry of colorEntries">
{{ entry.key }}: {{ entry.value }}
</div>
Converting once in the component (rather than via a pipe evaluated on every change-detection cycle) is worth doing for a large, rarely-changing object — an impure pipe re-runs on every change detection pass by default, which for a big object can add measurable overhead compared to a conversion done once and cached as a component property.
5. Iterating a Map instead of a plain object
colorMap = new Map([
['red', '#ff0000'],
['green', '#00ff00'],
]);
<div *ngFor="let entry of colorMap | keyvalue">
{{ entry.key }}: {{ entry.value }}
</div>
A native Map preserves insertion order reliably (unlike plain object key order, which has some JavaScript-specification quirks around numeric-looking keys) and the same keyvalue pipe works on it identically — worth considering as the data structure itself, not just a workaround, when key order genuinely matters.
6. With the newer @for control-flow syntax
@for (entry of colors | keyvalue; track entry.key) {
<div>{{ entry.key }}: {{ entry.value }}</div>
}
The keyvalue pipe works identically with Angular's newer built-in @for syntax — only the loop syntax itself changes; the object-to-array conversion problem and its solution stay exactly the same.