Skip to main content

RxVirtualView

Developer preview

This feature is under developer preview. It won't follow semver.

RxVirtualView virtualizes arbitrary DOM by rendering an element's content only while it is visible in the viewport (via IntersectionObserver) and swapping it for a lightweight placeholder while it is out of view. Unlike list virtualization (rxVirtualFor), it works for masonry layouts, dynamic grids, and widget-based landing pages where the items are heterogeneous. Content and placeholder rendering are scheduled through the concurrent render strategies.

Why this matters: see Concurrent scheduling & the frame budget.

Import

import { RxVirtualView, RxVirtualViewContent, RxVirtualViewObserver, RxVirtualViewPlaceholder, RxVirtualViewConfig, provideVirtualViewConfig } from '@rx-angular/template/virtual-view';

RxVirtualView works together with three sibling directives. rxVirtualViewObserver is mandatory and must be an ancestor of rxVirtualView.

DirectiveSelectorRole
RxVirtualViewObserver[rxVirtualViewObserver]Hosts the IntersectionObserver, cache, and resize services. Mandatory ancestor.
RxVirtualView[rxVirtualView]The observed node whose content/placeholder is swapped by visibility. exportAs: 'rxVirtualView'.
RxVirtualViewContent[rxVirtualViewContent]Template rendered while the node is visible.
RxVirtualViewPlaceholder[rxVirtualViewPlaceholder](Optional) template rendered while the node is not visible.

RxVirtualViewObserver Inputs

InputTypeDefaultDescription
rootElementRef \| HTMLElement \| nullthe host elementThe element the IntersectionObserver is applied to. Pass null to observe against the browser viewport. See root.
rootMarginstring''Margin around the root. See rootMargin.
scrollMarginstringconfig scrollMargin ('100px')Margin applied to nested scroll containers between the target and the root. See scrollMargin.
thresholdnumber \| number[]0Percentage(s) of target visibility at which the observer callback runs. See threshold.

RxVirtualView Inputs

InputTypeDefaultDescription
cacheEnabledbooleanconfig cacheEnabled (true)Cache detached contents and placeholders to optimize view rendering.
startWithPlaceholderAsapbooleanconfig startWithPlaceholderAsap (false)Render the placeholder immediately, without waiting for the content to become visible. Counters concurrent-render flickering.
keepLastKnownSizebooleanconfig keepLastKnownSize (false)Keep the last known size of the host element by setting min-height/min-width while the placeholder is shown.
useContentVisibilitybooleanconfig useContentVisibility (false)Add content-visibility: auto to the host together with contain-intrinsic-width/contain-intrinsic-height.
useContainmentbooleanconfig useContainment (true)Add the contain CSS property: size layout paint when useContentVisibility is true and the placeholder is visible; content otherwise.
placeholderStrategyRxStrategyNames<string>config placeholderStrategy ('low')The render strategy used to render the placeholder. See render strategies.
contentStrategyRxStrategyNames<string>config contentStrategy ('normal')The render strategy used to render the content. See render strategies.
extractSize(entry: ResizeObserverEntry) => { width: number; height: number }border-box sizeFunction extracting width & height from a ResizeObserverEntry.
resizeObserverOptionsResizeObserverOptionsOptions passed to the underlying ResizeObserver.

RxVirtualView Outputs

OutputTypeDescription
visibilityChanged{ content: boolean; placeholder: boolean }Emits whenever the virtual view transitions between showing content and showing placeholder. The emitted value is the current visibility state of both templates.

RxVirtualView members

MemberTypeDescription
visibility{ content: boolean; placeholder: boolean } (getter)Synchronous read of the current visibility state. Accessible in the template via exportAs="rxVirtualView".

RxVirtualViewObserver methods

MethodSignatureDescription
hideAll(): voidForce all virtual views in this observer to hide (e.g. when a modal opens).
showAllVisible(): voidRestore visibility after hideAll(). Must be called to undo hideAll().

RxVirtualViewConfig

Interface representing all configuration that can be adjusted on a provider level.

export interface RxVirtualViewConfig {
enabled: boolean | Signal<boolean>;
keepLastKnownSize: boolean;
useContentVisibility: boolean;
useContainment: boolean;
placeholderStrategy: RxStrategyNames<string>;
contentStrategy: RxStrategyNames<string>;
cacheEnabled: boolean;
startWithPlaceholderAsap: boolean;
scrollMargin: string;
enableAfterHydration: boolean;
cache: {
/** The maximum number of contents that can be stored in the cache. Defaults to 20. */
contentCacheSize: number;
/** The maximum number of placeholders that can be stored in the cache. Defaults to 20. */
placeholderCacheSize: number;
};
}
FieldTypeDescription
enabledboolean \| Signal<boolean>Whether the virtual view is active. When false, content is rendered synchronously with no IntersectionObserver (useful for SSR/hydration).
enableAfterHydrationbooleanWhen the directive starts disabled and later becomes enabled: if true, register the visibility observer and virtualize; if false, keep showing content and never swap to placeholders.
scrollMarginstringDefault scroll margin used by RxVirtualViewObserver.
placeholderStrategyRxStrategyNames<string>Default placeholder render strategy.
contentStrategyRxStrategyNames<string>Default content render strategy.

For the SSR/hydration usage of enabled and enableAfterHydration, see How to use RxVirtualView with hydration.

Customize the config

Use provideVirtualViewConfig at any provider level (component, appConfig, route, …). Provided values are merged over the defaults.

import { ApplicationConfig } from '@angular/core';
import { provideVirtualViewConfig } from '@rx-angular/template/virtual-view';

const appConfig: ApplicationConfig = {
providers: [
provideVirtualViewConfig({
/* your custom configuration */
}),
],
};

provideVirtualViewConfig also accepts a factory function, evaluated in an injection context, for signal-driven config (see the hydration how-to).

Default configuration

Used when no other config is provided.

{
enabled: true,
keepLastKnownSize: false,
useContentVisibility: false,
useContainment: true,
placeholderStrategy: 'low',
contentStrategy: 'normal',
startWithPlaceholderAsap: false,
cacheEnabled: true,
enableAfterHydration: true,
scrollMargin: '100px',
cache: {
contentCacheSize: 20,
placeholderCacheSize: 20,
},
}

Minimal example

Render a widget when it is visible, otherwise a placeholder that reserves its size:

<!-- observe against the browser viewport -->
<div rxVirtualViewObserver [root]="null">
<div class="widget" rxVirtualView>
<widget *rxVirtualViewContent />
<div *rxVirtualViewPlaceholder style="min-height: var(--rx-vw-h, 100px); min-width: var(--rx-vw-w, 50px);"></div>
</div>
</div>

After the content renders once, the --rx-vw-h / --rx-vw-w CSS variables hold its measured dimensions, so the placeholder can match its size and avoid layout shift.

See also