Choosing Between TableView and ListView in Titanium SDK for Large Data Sets
A decision guide for Titanium SDK developers choosing between TableView and ListView for large data sets. Covers performance, memory, feature differences, and a concrete ListView implementation with validation steps.
05 Sept 2025, 03:21 UTC

The Decision: Which Component Handles Large Lists Better?
When rendering more than a few hundred rows in a Titanium mobile app, the choice between TableView and ListView directly affects memory consumption, scroll smoothness, and development effort. For data sets exceeding ~500 rows, ListView is the recommended choice because it virtualizes row rendering—only visible items exist as UI components—whereas TableView instantiates a component for every row regardless of visibility.
Constraints and Compatibility
- Titanium SDK version: ListView requires SDK 5.0 or later. Projects on older versions must use TableView or upgrade.
- Android API level: ListView relies on Android's RecyclerView backend; minimum API 14 (Android 4.0) is required, but API 21+ ensures full feature parity.
- iOS support: Both components work on iOS 10+, but ListView maps to UICollectionView for virtualization.
- Existing codebase: Migrating from TableView to ListView involves rewriting row creation logic to use
ListSectionandListItemtemplates.
Quick Comparison
| Factor | TableView | ListView |
|---|---|---|
| Row instantiation | All rows created at load | Only visible rows created |
| Memory footprint (2000 rows) | ~150–300 MB | ~15–30 MB |
| Scroll performance | Degrades after ~500 rows | Consistent 60 fps |
| Section/sticky header support | Manual implementation | Built-in via ListSection |
| Per-row customization | Full control per row | Template-based, CSS styling |
| Event handling | Individual row listeners | Delegated to ListView |
| Legacy Android layout support | Yes | Limited |
Trade-offs in Practice
When TableView Still Makes Sense
- Data sets under 200 rows where virtualization overhead isn't justified.
- Highly heterogeneous rows where each row needs completely different layouts or native modules.
- Maintenance-only projects on Titanium SDK < 5.0 with no upgrade path.
When ListView Wins
- Lists exceeding 500 rows—memory and frame-rate gains are immediate.
- Sectioned data with sticky headers (contacts, settings, grouped feeds).
- Frequent data updates—
ListSection.replaceItemsAt()orappendItems()modify only affected items without full reload. - Cross-platform consistency—templates enforce uniform row structure.
Concrete Implementation: ListView with 2000 Items
The following example shows a minimal ListView setup using a template and a single ListSection. Run this in a Titanium Alloy controller or classic app.js.
// app/controllers/index.js (Alloy) or app.js (classic)
const win = Ti.UI.createWindow({ backgroundColor: '#fff' });
// 1. Define a template — maps data keys to view properties
const template = {
childTemplates: [
{ type: 'Ti.UI.Label', bindId: 'title', properties: { left: 16, top: 12, font: { fontSize: 16 }, color: '#222' } },
{ type: 'Ti.UI.Label', bindId: 'subtitle', properties: { left: 16, top: 36, font: { fontSize: 13 }, color: '#666' } }
]
};
// 2. Create ListView with the template
const listView = Ti.UI.createListView({
templates: { 'default': template },
defaultItemTemplate: 'default',
separatorStyle: Ti.UI.LIST_SEPARATOR_STYLE_SINGLE_LINE
});
// 3. Build a section and populate items
const section = Ti.UI.createListSection();
const items = [];
for (let i = 0; i < 2000; i++) {
items.push({
title: { text: `Item ${i + 1}` },
subtitle: { text: `Description for row ${i + 1}` },
properties: { itemId: i, accessoryType: Ti.UI.LIST_ACCESSORY_TYPE_DISCLOSURE }
});
}
section.setItems(items);
listView.sections = [section];
// 4. Handle item click via delegated event
listView.addEventListener('itemclick', (e) => {
const item = section.getItemAt(e.itemIndex);
alert(`Tapped: ${item.title.text}`);
});
win.add(listView);
win.open();
Key Points in the Code
childTemplatesdefines reusable view hierarchy; eachbindIdlinks to a key in the item data object.setItems()accepts an array of plain objects—noTableViewRowinstances created.- Event listener attaches once to the
ListView, not per row. itemIdin properties provides a stable identifier for updates.
Validation: How to Verify Virtualization Works
Use the device profiler (Android Studio Profiler or Xcode Instruments) to confirm ListView renders only visible rows:
- Build the example above in a clean Titanium project (
ti build -p androidor-p ios). - Open the profiler, filter for
TiUIListVieworRecyclerView(Android) /UICollectionView(iOS). - Scroll rapidly through the 2000 rows. Observe the View count or Allocation count—it should plateau around 10–15 views (visible + buffer), not grow to 2000.
- Repeat with a TableView equivalent (create 2000
TableViewRowobjects) and compare memory growth.
Expected result: ListView maintains a near-constant view count; TableView shows linear growth with each row scrolled into view for the first time.
Limitations and Gotchas
- Template rigidity: Complex per-row logic (e.g., conditional child views) requires multiple templates and
templateproperty per item, adding complexity. - CSS styling: ListView row styling often needs platform-specific CSS (e.g.,
app.tss) for consistent padding, separator insets, or accessory alignment. - No direct row access: You cannot call
row.animate()or modify a row's subviews directly; usesection.updateItemAt()with new property values. - Pull-to-refresh: Must be implemented via
RefreshControlwrapper; not built into ListView.
Rollback Consideration
Switching from TableView to ListView is a one-way refactor—row creation, event handling, and data binding patterns change entirely. If you must revert, keep the TableView branch in version control. No runtime rollback is possible because the component APIs are incompatible.
Bottom Line
For any new Titanium SDK 5.0+ project displaying more than a few hundred rows, start with ListView. The virtualization model eliminates the memory and performance ceiling that TableView hits, and the template system scales cleanly with sectioned data. Reserve TableView for small, static, or highly irregular lists where the migration cost outweighs the benefit.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.