Page navigation lifecycle and the backstack
For one forward navigation followed by back, a page fires exactly this sequence:
navigatingTo → loaded → navigatedTo ... navigatingFrom → unloaded → navigatedFrom
What is true at each step:
navigatingTo— earliest hook;args.context(theNavigationEntry.context) is available. SetbindingContexthere.loaded— the view tree exists and native views are attached, butFrame.topmost().currentPageis NOT this page yet. Don't read "the current page" here expecting yourself.navigatedTo— navigation committed;currentPageis now this page. Start animations, timers, subscriptions here.navigatingFrom/unloaded/navigatedFrom— noteunloadedfires BEFOREnavigatedFrom. Teardown that needs the native view must happen byunloaded;navigatedFromis last and runs on an already-unloaded page.- After a back-pop the page is fully torn down:
parent,frameand the native context are null,isLoadedis false. Never cache a popped Page for reuse. page.navigationContextis cleared on back navigation — read what you need from it before leaving, or copy it onto your view model.
import { EventData, NavigatedData, Page } from '@nativescript/core';
export function navigatingTo(args: NavigatedData) {
const page = <Page>args.object;
if (args.isBackNavigation) return; // returning — state already built
page.bindingContext = createViewModel(args.context);
}
Navigating with context
Frame.topmost().navigate({
moduleName: 'views/detail/detail-page',
context: { id: item.id }, // arrives as args.context / page.navigationContext
backstackVisible: true, // default; false = skipped on back
clearHistory: false,
});
clearHistory: trueempties the backstack; the outgoing page fires onlynavigatingFrom/navigatedFrom(no unload pair queued behind a back button that no longer exists) andframe.backStack.lengthbecomes 0.backstackVisible: falseentries still construct and navigate normally — the page factory runs — they are only skipped when going back.frame.goBack()whencanGoBack()is false is a safe no-op. After N forward navigations where the last wasbackstackVisible: false, you get N−1 successfulgoBack()s.- Jump multiple screens back with
frame.goBack(frame.backStack[0])(pass the target entry). isBackNavigationon intermediate skipped pages is stillforward-shaped on the from-events — don't branch teardown on it; branch setup on it innavigatingTo(the classic guard above).
Frames
Frame.topmost()follows whichever frame navigated most recently — inside a TabView it's the selected tab's frame, not the app root (seens-tabview-lazy-loading).- The Frame itself re-emits only
navigatingTo/navigatedTo— never the From pair. Listen on the Page for those.
Where work belongs
| Work | Event |
|---|---|
| Build/bind view model from context | navigatingTo (guard isBackNavigation) |
| Measure views, access nativeView | loaded |
| Start timers/subscriptions/analytics | navigatedTo |
| Stop timers, detach native listeners | unloaded |
| Final bookkeeping (no native access) | navigatedFrom |
Verified 2026-08 against @nativescript/core 9.1.0-rc.3: event order, currentPage timing, context clearing, teardown state and canGoBack behavior are asserted in apps/automated/src/ui/page/page-tests-common.ts, ui/frame/frame-tests-common.ts and navigation/navigation-tests.ts; not re-run standalone.