[readonly] markdown buffer
Virtualized LWC Lists for 100,000 Alarms
Summer '26 introduces lightning-dynamic-list-container and lightning-dynamic-list-item in Developer Preview. The useful question is not whether they can render 5,000 rows. It is whether a user can explore 100,000 alarms without the browser loading and rendering all of them.
The answer depends on three separate limits.
The three counts
Virtualisation controls the third count. Server paging and small payloads control the second. You need both.
Five parts of a scalable list
1. Let the container own scrolling
The preview API wraps each stable item in a dynamic-list container:
<lightning-dynamic-list-container
onrenderlistitems={handleRenderListItems}
onloadmore={handleLoadMore}>
<template for:each={visibleAlarms} for:item="alarm">
<lightning-dynamic-list-item key={alarm.id} item-id={alarm.id}>
<c-alarm-row alarm={alarm}></c-alarm-row>
</lightning-dynamic-list-item>
</template>
</lightning-dynamic-list-container>
Give the list a bounded height and avoid adding a competing scroll wrapper. Virtualisation relies on the container understanding row and viewport positions.
2. Render only the requested slice
When the container reports a visible range, slice the loaded buffer rather than recreating the whole list.
handleRenderListItems(event) {
const { startIndex, endIndex } = event.detail;
this.visibleAlarms = this.loadedAlarms.slice(startIndex, endIndex + 1);
}
Because this is Developer Preview, confirm the final event-detail names in the current Component Library before implementation.
3. Page summaries from the server
Do not fetch 100,000 full records merely because only 40 are rendered. Load compact row summaries in pages and fetch detail when a row opens.
For an operational feed, keyset pagination is usually steadier than large offsets:
List<Alarm__c> alarms = [
SELECT Id, Name, Severity__c, Last_Seen_At__c
FROM Alarm__c
WHERE Last_Seen_At__c < :cursorTime
OR (Last_Seen_At__c = :cursorTime AND Id < :cursorId)
WITH USER_MODE
ORDER BY Last_Seen_At__c DESC, Id DESC
LIMIT :pageSize
];
The sort and filters still need a selective query shape. A virtualised UI cannot rescue a slow backend.
4. Search on the server
Filtering a few hundred loaded rows in JavaScript is fine. Searching a 100,000-row dataset is not. Send deliberate filters to the server, reset the cursor and return a new first page.
This also avoids a misleading interface where search only covers the records already loaded.
5. Test it as an operational tool
Measure initial render, DOM nodes, memory after extended scrolling, query time and mobile behaviour. Test keyboard focus near the top, middle and end.
Also decide what happens when new alarms arrive while the user is reading. Automatically prepending rows can move the viewport. A “new alarms” indicator may be safer than stealing the user's position.
Where it fits
Dynamic lists suit alarm consoles, dispatch queues, activity feeds, device directories and rich lookup results: many rows, custom presentation and scan-then-drill behaviour.
They are not automatically a replacement for lightning-datatable. If users need grid semantics, column resizing and inline editing, a table may still be the right tool.
For 100,000 alarms, the goal is not to display 100,000 rows at once. It is to make them explorable: virtualise the DOM, page compact data, search on the server and preserve the user's focus while the dataset changes.