Skip to main content

RxFor

RxFor renders a list concurrently: each item template is a single renderable unit that is prioritized, frame-budgeted, and scheduled through the CDK render strategies, so a large or expensive list renders without blocking the UI thread. Rendering is cancelable, and a renderCallback reports exactly when a set of changes has hit the DOM.

Native @for is synchronous: it has no equivalent for non-blocking, cancelable, scheduled list rendering (angular#43168). Reach for RxFor when a list is large enough that synchronous rendering janks, when you need a render hook, or when you want per-directive render scheduling. For small static lists, native @for is the simpler choice.

Import

import { RxFor } from '@rx-angular/template/for';

Selector: [rxFor][rxForOf]

Usage

rxFor accepts an ObservableInput, a Signal, or a static value. You don't need to unwrap a signal; pass its reference and rxFor reads it for you.

src/list.component.html
<div class="movie-list">
<movie *rxFor="let movie of movies" [movie]="movie" />
</div>
src/list.component.ts
import { Component, inject, Signal } from '@angular/core';
import { RxFor } from '@rx-angular/template/for';

@Component({
templateUrl: './list.component.html',
imports: [RxFor],
})
export class ListComponent {
private readonly movieService = inject(MovieService);
// an Observable, a Signal, or a static array all work
movies: Signal<Movie[]> = this.movieService.fetchMovies();
}

trackBy shortcut

Pass a TrackByFunction, or name a property of the item type as a string shorthand.

<!-- string shorthand -->
<movie *rxFor="let movie of movies$; trackBy: 'id'" [movie]="movie" />

<!-- TrackByFunction -->
<movie *rxFor="let movie of movies$; trackBy: trackMovie" [movie]="movie" />
trackMovie(index: number, movie: Movie) {
return movie.id;
}

Context variables

<ul>
<li
*rxFor="
let item of items$;
trackBy: 'id';
let count = count;
let index = index;
let first = first;
"
>
{{ index }} / {{ count }}: {{ item.name }}
</li>
</ul>

Tune how it renders: the strategy, renderCallback, parent, and patchZone inputs are documented together in How to tune rendering with strategies. To keep tests deterministic, see How to test scheduled rendering.

Inputs

InputTypeDefaultDescription
rxForOfObservableInput<U> \| Signal<U> \| U (U extends NgIterable<T>)The iterable to render. Accepts an Observable/Promise, a Signal, or a static value.
trackBykeyof T \| ((index: number, item: T) => unknown)Item identity. Provide a TrackByFunction, or name a property as a string shorthand.
strategyObservable<RxStrategyNames<string>> \| RxStrategyNames<string>normalThe render strategy used to schedule change detection. See tune rendering with strategies.
renderCallbackSubject<U>A Subject that emits the rendered item set whenever rxFor finishes creating, updating, or removing templates. Useful for post-render DOM work.
patchZonebooleantrueIf false, RxFor operates outside NgZone. Inert under zoneless change detection. See tune rendering with strategies.
parent (deprecated)booleanfalseIf true, RxFor informs its host component about template changes so @ViewChild/@ContentChild queries resolve. Deprecated: not needed with signal-based view/content queries.

parent default is false. It is sourced from the render-strategies config (parent: false). The flag is deprecated and will be removed; with signal-based view and content queries you should leave it false. See How to tune rendering with strategies for when a legacy query still needs parent: true.

Context variables

Available on every item template:

Static context variables (mirrored from NgFor)

VariableTypeDescription
$implicitTThe item value, accessed by let val.
indexnumberCurrent index of the item.
countnumberNumber of items in the list.
firstbooleantrue if the item is first.
lastbooleantrue if the item is last.
evenbooleantrue if index % 2 === 0.
oddbooleanThe opposite of even.

Reactive context variables

VariableTypeDescription
item$Observable<T>The item value as an Observable.
index$Observable<number>index as an Observable.
count$Observable<number>count as an Observable.
first$Observable<boolean>first as an Observable.
last$Observable<boolean>last as an Observable.
even$Observable<boolean>even as an Observable.
odd$Observable<boolean>odd as an Observable.
select(keys: (keyof T)[]) => Observable<any>Returns a selection function for a subset of item properties as a distinct Observable, used for nested rxFor.

Features

DX

  • Reduces async-pipe boilerplate and handles null/undefined uniformly.
  • Works with static values, Observables, and Signals.
  • Supports immutable and mutable data via trackBy.
  • Provides a full set of static and reactive context variables.

Performance

  • Non-blocking, lazy template creation via render strategies.
  • Configurable frame budget (defaults to 60 FPS).
  • Triggers change detection at the EmbeddedView level.
  • Distinct-in-a-row values avoid over-rendering.
  • Cancels scheduled work when an item is removed or superseded.
  • Nested lists re-render only what changed.

Reconciliation

By default rxFor uses Angular's IterableDiffer to compute list operations. You can opt in to the reconciliation algorithm shipped with the native @for control flow, which diffs move/swap operations with fewer operations and less memory. Under concurrent mode it works by detaching and attaching views (rather than moving DOM), so a full shuffle can briefly show views detached before re-attach.

import { provideExperimentalRxForReconciliation } from '@rx-angular/template/for';

export const appConfig: ApplicationConfig = {
providers: [provideExperimentalRxForReconciliation()],
};

To opt out again at some level of the injector tree, use provideLegacyRxForReconciliation().

Resources

See also