A screen reads and reacts to navigation state through five small primitives: the navigator
handle, the current route, whether the screen is focused right now, an effect that re-runs on
focus/blur, and a live selector over the navigator’s state. Every adapter names them per its own
framework idiom — React, Vue, and Svelte use useX(), Angular uses injectX(). That’s not a
typo: Angular’s own ecosystem convention names an inject()-based
function injectX, the same way this site’s Angular guide documents for
every other Angular-adapter API. Svelte calls this bucket “runes” rather than “hooks”, but the
function names are identical to React’s/Vue’s. All four read from the nearest enclosing
<Stack>, <Tab>, or <Drawer> screen and throw if called outside one.
useRoute/injectRoute returns the current screen’s route (name, key, params); useNavigation/
injectNavigation returns the imperative handle for whichever navigator mounted the screen
(push/pop/… for a Stack screen, jumpTo for a Tab screen, openDrawer/… for a
Drawer screen). Each framework hands back a different shape, matching how that framework
tracks changing values:
useIsFocused/injectIsFocused reports whether the screen is focused right now.
useFocusEffect/injectFocusEffect runs an effect while the screen is focused and runs its own
returned cleanup on blur — the same shape as a plain effect, just re-armed on every focus/blur
pair instead of once on mount.
useIsFocused/injectIsFocused always starts false. It never guesses focus from stack
position — it only flips true once the route’s real focus event fires, which can lag one
microtask behind mount. That’s intentional: it matches the async timing of a real native screen
transition, not an instant JS-only mount.
useNavigationState/injectNavigationState takes a selector over the navigator’s full
INavigatorState and returns (a wrapper over) the selected value, updating whenever the
navigator’s state changes.
getParent() walks exactly one hop up to the enclosing navigator when navigators are nested —
for example a Tab navigator mounted as the content of a Stack screen. There’s no multi-hop or
named-ancestor lookup; a screen two levels deep needs to call getParent() on the handle
getParent() already returned.
navigation here is the Tab screen’s own handle (jumpTo/setParams, already concretely
typed via useTabNavigation()); getParent() still returns the IAnyNavigatorHandle union
though, since — unlike the navigator that mounted this screen — it doesn’t statically know what
kind of navigator is above it. This is the one place a raw guard is genuinely unavoidable, so
isStackNavigatorHandle()/isTabNavigatorHandle()/isDrawerNavigatorHandle() live in the
framework-agnostic core and are importable from the bare @symbiote-native/navigation package
(the same place IRoute/INavigatorState come from), not from /react//vue//angular//svelte.
Vue’s equivalent reads navigation.value.getParent(); Svelte’s reads
navigation.current.getParent(); Angular’s reads injectTabNavigation().getParent() — same
one-hop semantics in all three.
useNavigation() narrowed to a Drawer handle; throws if the nearest navigator isn’t a Drawer
isStackNavigatorHandle(handle)/isTabNavigatorHandle(handle)/isDrawerNavigatorHandle(handle)
are framework-agnostic type guards importable from the bare @symbiote-native/navigation package
(not /react//vue//angular//svelte) — the same guards useStackNavigation()/etc. use internally,
exposed for the one case they don’t cover: narrowing a getParent() result, which is always a
union since it doesn’t statically know the enclosing navigator’s kind. See
Nested navigators and getParent() above.
Runs effect on focus, runs its returned cleanup on blur. No memoization needed — a Svelte component’s script runs exactly once, so the closure is captured a single time by value
Selects a value out of the navigator’s live state; only <Stack> broadcasts live updates, <Tab>/<Drawer> stay on the initial snapshot
Signature
Description
useNavigationState<T>(selector: (state: INavigatorState) => T): { readonly current: T }
Selects a value out of the navigator’s live state; only <Stack> broadcasts live updates, <Tab>/<Drawer> stay on the initial snapshot. Boxed getter, read .current