Framework7 Virtual Lists: Render Large Data Sets Without DOM Bloat
Framework7's Virtual List renders only visible items and recycles DOM nodes, preventing DOM bloat when scrolling through thousands of records.
08 Jun 2026, 20:55 UTC

The Problem: DOM Bloat on Long Lists
Rendering thousands of Framework7 list items creates thousands of DOM nodes. On mobile devices, this causes slow initial paint, janky scrolling, and high memory usage. The useful takeaway is to use Framework7's Virtual List component, which renders only the items currently in the viewport and recycles their DOM nodes as the user scrolls.
Worked Configuration: Virtual List with Static Item Height
Framework7's Virtual List requires a container element, an item height, and a data array. It does not create a DOM node for every item; instead, it maintains a small pool of cells and updates their content based on scroll position.
<div class="page">
<div class="navbar">
<div class="navbar-inner">
<div class="title">Users</div>
</div>
</div>
<div class="page-content">
<div class="list virtual-list">
<ul>
<li class="virtual-list-cell"></li>
</ul>
</div>
</div>
</div>
// Run this script after Framework7 is initialized, typically in your app's
// page init handler or main module. No build step is required for plain JS.
var app = new Framework7({
root: '#app',
});
function generateUsers(count) {
var users = [];
for (var i = 1; i <= count; i++) {
users.push({
id: i,
name: 'User ' + i,
email: 'user' + i + '@example.com',
});
}
return users;
}
var virtualList = app.virtualList.create({
el: '.virtual-list',
items: generateUsers(10000),
height: 46, // fixed height for each list item in pixels
renderExternal: function (index, item) {
return '<li>' +
'<div class="item-content">' +
'<div class="item-inner">' +
'<div class="item-title">' + item.name + '</div>' +
'<div class="item-after">' + item.email + '</div>' +
'</div>' +
'</div>' +
'</li>';
},
});
Place the script in your page's <script> block or module after the Framework7 instance exists. If you use a bundler, import Framework7 and call app.virtualList.create once the DOM is ready.
Serving and Verifying Locally
Run a static file server from your project root to test the page:
# Run in the project root directory. No elevated permissions required.
npx serve . -l 5000
Expected check: open http://localhost:5000, open DevTools, and inspect the .virtual-list element while scrolling. The DOM should contain only a small, stable number of <li> nodes (typically a few dozen) regardless of the 10,000-item array. Risk: port 5000 may already be in use; choose another port with -l 3000 if needed.
Updating Data and Handling Interaction
When the underlying data changes, call replaceAllItems so the virtual list recalculates visible cells:
virtualList.replaceAllItems(generateUsers(500));
Avoid storing form input state or focus inside virtual list cells. Because cells are recycled, an input in cell 5 may later display data for item 500 after scrolling. Keep interactive state outside the cell DOM, keyed by item ID.
Limits and Common Mistakes
- Fixed item height: Virtual List performs best when every item has the same height. Variable heights are supported but require additional measurement and can reduce scroll performance.
- Recycled DOM nodes: Do not attach permanent event listeners directly to cell elements. Use event delegation on the list container or Framework7's component lifecycle if using Vue/React/Svelte.
- Initial data size: Passing a massive array still costs memory for the data itself. For hundreds of thousands of records, paginate or fetch data in chunks before assigning it to the virtual list.
- Focus and scrolling: Programmatically focusing an input inside a virtual cell can scroll the wrong element into view because the cell's position changes during recycling.
Practical Verification Steps
- Open DevTools > Elements and confirm only a small subset of
<li>nodes exist while the list reports thousands of items. - Scroll to the bottom and back to the top; confirm cell content updates correctly and no blank cells appear.
- Call
replaceAllItemswith a smaller array and verify the scrollbar length and visible items update. - Monitor the Memory tab in DevTools during extended scrolling to confirm node count does not grow continuously.
Limitations
| Limitation | Impact |
|---|---|
| Cell recycling | DOM nodes are reused; do not rely on cell-specific state or persistent event listeners. |
| Item height | Variable heights require extra calculation; fixed heights give the smoothest scrolling. |
| Data memory | The full items array remains in memory; combine with pagination for very large datasets. |
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.