TabView lazy loading: the platform split
Android preloads 1 tab to each side of the selected tab by default; iOS cannot preload at all. The framework's own tests assert the divergence: after navigating away from and back to a page hosting a two-tab TabView, the unselected tab's content has fired loaded twice and unloaded once on Android — and zero times on iOS.
Consequences of leaving the default:
- "Fetch on tab
loaded" fetches for tabs the user never opened — on Android only. - A heavy neighbouring tab (map, webview, long list) is built during first paint of the selected tab — on Android only.
- Your loaded/unloaded counts differ per platform, so lifecycle-coupled logic drifts.
The normalizer
import { TabView } from '@nativescript/core';
tabView.androidOffscreenTabLimit = 0; // both platforms now load only the selected tab
<TabView androidTabsPosition="bottom" androidOffscreenTabLimit="0">
With the limit at 0, creating the TabView raises events for the selected tab only — the other tabs' frames stay cold until first selection. (Trade-off: first switch to a tab now pays its build cost at switch time; keep tab roots light or show a skeleton.)
Idempotent per-tab init
Even normalized, loaded fires again every time content re-enters the visual tree (tab re-selected after being far away, page re-navigated). Guard one-time work:
const initialized = new Set<string>();
export function onTabContentLoaded(args: EventData) {
const key = (<View>args.object).id;
if (initialized.has(key)) return;
initialized.add(key);
loadDataForTab(key);
}
Related facts
Frame.topmost()follows the selected tab's frame once tabs contain frames — switchingselectedIndexflips which frame is "topmost". Don't cache it across tab switches (seens-page-navigation-lifecycle).- Every
TabViewItemmust have a non-nullviewwhenitemsis assigned, or the assignment throws. selectedIndexdefaults to-1before items are set; assigning a non-emptyitemsauto-selects index 0 (yourselectedIndexChangedhandler's first event reportsoldIndex: 0).
Verified 2026-08 against @nativescript/core 9.1.0-rc.3: the preload asymmetry ([2,0]/[1,0] iOS vs [2,2]/[1,1] Android loaded/unloaded counts), the limit-0 event trace, and Frame.topmost() behavior are asserted in apps/automated/src/ui/tab-view/tab-view-navigation-tests.ts and tab-view-root-tests.ts; not re-run standalone.