Framework7 Virtual List: Bounding DOM Size for Large Mobile Datasets
Rendering thousands of rows in a mobile web app usually kills scroll performance. Framework7 Virtual List offers a bounded-DOM alternative—if you respect its lifecycle and measurement constraints.
17 Jul 2026, 06:06 UTC

When a 5,000-row list becomes a mobile performance problem
You have a dataset of several thousand records—orders, messages, sensor readings—and you render them as a standard HTML list. On desktop it feels acceptable. On a mid-range phone, scrolling stutters, memory climbs, and eventually the tab reloads. The root cause is not JavaScript logic; it is the sheer number of DOM nodes the browser must style, lay out, and keep in memory.
Framework7's Virtual List component exists to solve exactly this class of problem. It is not a styling library or a data-fetching tool. It is an engineering decision to keep the rendered DOM small while presenting the illusion of an infinitely long scrollable list.
What Virtual List actually does
A native list with 5,000 items creates 5,000 DOM elements. Virtual List creates only the items currently visible in the viewport plus a small buffer above and below. As the user scrolls, it recycles or replaces those DOM nodes with the data for the newly visible rows. The total node count stays roughly constant whether the underlying array holds 100 items or 100,000.
Two configuration choices drive its behavior:
- Item rendering is handled by a template function (or template string, depending on version) that receives a data item and returns the HTML for one row.
- Row height can be a fixed pixel value or a callback that returns a height per item. The fixed-height path is simpler and more reliable; the dynamic path is necessary for wrapping text or variable content but introduces measurement complexity.
Why lifecycle matters in Framework7's Router
Framework7 Views and the Router cache pages to make navigation feel instant. That caching is convenient, but it means a page's DOM can persist after the user navigates away. If you instantiate a Virtual List every time pageInit fires without tearing it down on pageBeforeRemove, you accumulate orphaned instances and event listeners. Memory grows, and scroll handlers from old pages may still respond.
The practical pattern is to create the instance when the page initializes and destroy it before the page is removed from the DOM. When the page is revisited, a fresh instance is created against the current data.
A worked integration pattern
The following example assumes a recent Framework7 major version. Option names and initialization methods are version-sensitive across major releases, so verify the exact API surface against the documentation for your installed version before copying verbatim.
// Run inside your page component or page event handler
// Requires: Framework7 app instance available as `app`
var largeDataset = []; // populate with your records
for (var i = 0; i < 5000; i++) {
largeDataset.push({ id: i, title: 'Item ' + i });
}
var virtualList = null;
// pageInit fires when the page is initialized
$$(document).on('page:init', '.page[data-name="large-list"]', function (e) {
var page = e.detail;
virtualList = app.virtualList.create({
el: page.$el.find('.virtual-list')[0],
items: largeDataset,
// Template function returns HTML for one row
renderItem: function (item) {
return '<li class="item-content"><div class="item-inner">' +
'<div class="item-title">' + item.title + '</div>' +
'</div></li>';
},
// Fixed height is safest for uniform rows
height: 48
});
});
// pageBeforeRemove fires before the page leaves the DOM
$$(document).on('page:beforeRemove', '.page[data-name="large-list"]', function (e) {
if (virtualList) {
virtualList.destroy();
virtualList = null;
}
});
If you pair this with pull-to-refresh or infinite scroll, do not rebuild the list. Mutate the underlying array and call updateItems() so Virtual List recalculates which rows are visible without destroying the instance.
// Infinite scroll handler example
function onInfiniteScroll() {
// fetch more records, append to largeDataset
// then:
if (virtualList) {
virtualList.updateItems();
}
}
Trade-offs you accept when you virtualize
Virtual List is not free. The main costs are in complexity and capability:
| Concern | Impact |
|---|---|
| Variable row heights | Dynamic height callbacks must be accurate. Bad estimates cause the scroll position to jump and rows to appear misaligned. |
| Accessibility | Screen readers generally cannot reach off-screen items because those items are not in the DOM. You may need to provide an alternative navigation path or summary for assistive technology. |
| Browser find-in-page | Native find-in-page cannot locate text in rows that are not currently rendered. |
| API stability | Option names and initialization patterns have changed across Framework7 major versions. Code written for one major version may not run on another without modification. |
How to verify it is working
After integrating Virtual List, confirm the behavior on a target device rather than trusting the abstraction:
- Load the page with a large dataset and scroll through several screens. Inspect the DOM (via remote debugging) and count the
<li>elements inside the list container. The count should remain small and roughly constant, not grow with the dataset size. - Navigate away from the page so
pageBeforeRemovefires, then navigate back. Monitor memory usage in the browser's performance tools. If memory climbs after repeated visits, the destroy logic is not running correctly. - Compare scroll smoothness against a native list with the same dataset on the same device. If Virtual List does not feel smoother, the dataset may be small enough that the overhead is not justified.
Decision summary
Reach for Virtual List when your dataset is large enough that DOM node count is the bottleneck—typically hundreds or thousands of uniform rows in a mobile web view. Stick with a native list for short, static lists where the simplicity of find-in-page and accessibility outweighs performance. If you do virtualize, respect the page lifecycle, prefer fixed heights unless your content truly varies, and always verify DOM node counts on real hardware.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.