API reference
Every export of @sigx/lynx-navigation — functions, hooks, components and types.
The package is ESM-only with a single entry point (.). All inference flows from augmenting the Register interface with your route map; see Usage for the setup pattern.
Functions
defineRoutes
function defineRoutes<const T extends RouteMap>(routes: T): T
Defines a typed route registry. Returns its input verbatim — it exists purely for TypeScript inference, narrowing to a literal route map so useNav, useParams and <Link> can infer route names and per-route param/search schemas.
hrefFor
function hrefFor<K extends RoutesWithoutParams>(name: K, search?: RouteSearch<K>): Href<K>;
function hrefFor<K extends RoutesWithParams>(name: K, params: RouteParams<K>, search?: RouteSearch<K>): Href<K>;
Builds a typed Href for a route, validating params and search against the route's schema at runtime (throws on validation failure). Overloaded by whether the route declares a params schema. Requires an active route registry (a mounted NavigationRoot, or _setRouteRegistry in tests). The returned url is null when the route has no path template.
parseHref
function parseHref(url: string): Href | null
Parses a URL string (absolute scheme://… or pathname-only) into a typed Href against the registered routes' path templates. Returns null when no path matches or params/search fail validation. First registration-order match wins. Takes only (url) at runtime.
compilePath
function compilePath(template: string): CompiledPath
Compiles a path template (e.g. '/users/:id') into a CompiledPath with a regex and a format() method. Supports literal segments, :name params (matching [^/]+) and tolerated trailing slashes. Throws on an empty / non-string template or duplicate param names. Pure and memoizable. No wildcards, optional or typed params in v1.
_setRouteRegistry
function _setRouteRegistry(routes: RouteMap): void
Test / integration use, not part of the supported public API. Imperatively seeds the module-level URL bridge registry (for tests or deep-link bootstrap before a NavigationRoot mounts). Last write wins; NavigationRoot calls this on setup.
_clearRouteRegistry
function _clearRouteRegistry(): void
Test use, not part of the supported public API. Clears the module-level URL bridge route registry.
Hooks
useNav
const useNav: () => Nav
Accesses the innermost navigator handle, provided by NavigationRoot. Returns a Nav with reactive read getters (current, stack, canGoBack) and mutators (push, replace, pop, popTo, popToRoot, reset, dismiss). Throws when no NavigationRoot is mounted.
useParams
function useParams<K extends RouteId>(_name: K): RouteParams<K>
Reads typed params for the current screen, asserted against the named route. The name argument is a compile-time discriminator — a wrong route name is a TS error. Snapshot at setup (screens remount on navigation).
useSearch
function useSearch<K extends RouteId>(_name: K): RouteSearch<K>
Reads typed search / query params for the current screen, asserted against the named route. The name argument is a compile-time discriminator. Snapshot at setup.
useHardwareBack
function useHardwareBack(): void
Wires the Android hardware / system back button to the active navigator. Idempotent per nav tree (first registration wins). No-op on iOS and in non-native runtimes. NavigationRoot auto-wires this by default — call it only when you set hardwareBack={false}. Platform: Android.
useLinkingNav
function useLinkingNav(opts: UseLinkingNavOptions = {}): void
Bridges @sigx/lynx-linking URL events (cold-start getInitialURL plus warm-start 'url' events) into the navigator via parseHref. Call once inside a NavigationRoot subtree. Requires the optional @sigx/lynx-linking peer.
useIsFocused
function useIsFocused(): Computed<boolean>
Returns a reactive Computed<boolean> (read .value) — true while this screen is the visible top of its navigator and every ancestor (parent entry plus enclosing tab) is focused. Must be called inside a route component rendered by Stack; throws otherwise.
useFocusEffect
function useFocusEffect(cb: () => void | (() => void)): void
Runs cb when the screen gains focus and runs its returned cleanup on blur or unmount. Re-runs on each focus return. Use for analytics, subscriptions or video playback while visible.
useDidAppear
function useDidAppear(cb: () => void | (() => void)): void
The heavy-work sibling of useFocusEffect. Runs cb once the screen is focused and no transition is in flight — not before, and not on every focus regardless of motion; the returned cleanup runs on blur, on a new transition, or on unmount.
useFocusEffect cannot serve this case. useIsFocused is nav.current.key === myKey, and a push commits the stack immediately — so a focus effect fires while the slide is still running.
That matters more than it sounds. Component setup runs on the background thread, but the resulting op batch is applied on the main thread in one uninterruptible pass — the same thread driving the tween. Mount work scheduled on focus therefore starves the animation it is running under.
useFocusEffect(() => { analytics.screen('Chat'); }); // cheap — fine on focus
useDidAppear(() => { const s = subscribeToFeed(); return () => s.close(); });
Reach for useDidAppear when the work is expensive enough to be felt: opening a socket, decoding media, hydrating a long list.
useScreenOptions
function useScreenOptions(optionsOrFn: ScreenOptions | (() => ScreenOptions)): void
Imperatively merges ScreenOptions (title, header / gesture flags, sheet options) into the current entry. A plain object merges once; a function is a tracked effect that re-merges on signal change. Strictly additive (per-key writes).
useScreenChrome
function useScreenChrome(): ScreenChrome
Reactive read of the focused screen's options and slot fills, plus header helpers (title, headerShown, canGoBack, pop, header, headerLeft, headerRight). The public foundation for custom header components; every property is a getter. Resolves to the scoped entry inside an EntryScope, otherwise to the navigator's destination entry.
useNavSerializer
function useNavSerializer(options: UseNavSerializerOptions): void
Persists the navigator's root stack across launches via a NavStorageAdapter. On mount it loads and validates (version / shape / unknown-route) then applies with nav.reset(); it subscribes to nav.stack and saves debounced (default 250 ms). Only the root navigator is persisted in v1 (nested / per-tab stacks are not yet).
useTabs
const useTabs: () => TabsNav
Accesses the enclosing Tabs navigator: { active, setActive(name), tabs }. Reactive. Throws when called outside Tabs. setActive ignores unknown tab names (no-op).
useDrawer
const useDrawer: () => DrawerNav
Accesses the enclosing Drawer navigator: { isOpen, open(), close(), toggle() }. Reactive. Throws when called outside Drawer.
useSheetHeight
const useSheetHeight: () => SharedValue<number>
The live visible height in px of the top bottom sheet — 0 when none is open, rising to its detent height as the sheet opens or drags, tracking the finger frame for frame. Reveal px is the visible height, so there is no progress→px scaling and no per-sheet rebind: the identity is stable for consumers.
Bind it like any SharedValue. The canonical use is a composer bar that must sit above whichever is taller, the soft keyboard or the sheet:
const sheetH = useSheetHeight();
const lift = useDerivedValue([keyboardLift, sheetH], 'max');
useAnimatedStyle(barRef, lift, 'translateY', { factor: -1 });
Two separate translateY bindings would sum — see useDerivedValue.
Must be called under a <NavigationRoot>. Returns a constant-0 SharedValue when the navigator was created with animated={false}, since there is no sheet SV to track.
Components
Each component has its own page with usage, props, slots and events — see the Components catalog:
- Stack — the stack navigator; renders the enclosing stack in bound mode or mints a fresh nested navigator (per-tab back-stacks) in nested-owner mode.
- Tabs — a persistent tab navigator hosting
<Tabs.Screen>; tab bodies stay mounted to preserve per-tab state, and it providesuseTabs(). - Drawer — a minimal off-canvas drawer navigator with a
sidebaranddefaultslot; providesuseDrawer(). - NavigationRoot — the root of a navigator subtree; creates and provides a fresh navigator state from
routes(isolated per root). - Screen — declarative per-screen options and slot fills, plus its
Screen.Header/Screen.HeaderLeft/Screen.HeaderRight/Screen.TabBarItemsub-components. - Header — the headless default navigator header chrome (back button, title, header slots), switching content reactively.
- TabBar — the headless default chrome for
Tabs; unstyled buttons with accessibility wiring, overridable viarenderTab. - Link — declarative navigation, the JSX flavor of
nav.push, with the same per-route conditional typing.
Their prop types and related APIs remain documented below.
Constants
NAV_SNAPSHOT_VERSION
const NAV_SNAPSHOT_VERSION = 1
The current nav snapshot schema version (value 1). Stored in NavSnapshot.version; bump it to reject old snapshots (onRestoreError fires 'version').
Types
Nav
interface Nav {
push<K extends RoutesWithoutParams>(name: K, search?: RouteSearch<K>, options?: PushOptions): void;
push<K extends RoutesWithParams>(name: K, params: RouteParams<K>, search?: RouteSearch<K>, options?: PushOptions): void;
replace<K extends RoutesWithoutParams>(name: K, search?: RouteSearch<K>, options?: PushOptions): void;
replace<K extends RoutesWithParams>(name: K, params: RouteParams<K>, search?: RouteSearch<K>, options?: PushOptions): void;
pop(count?: number, options?: PopOptions): void;
popTo<K extends RouteId>(name: K): void;
popToRoot(): void;
reset(state: { stack: ReadonlyArray<StackEntry> }): void;
dismiss(): void;
readonly current: StackEntry;
readonly stack: ReadonlyArray<StackEntry>;
readonly canGoBack: boolean;
readonly parent: Nav | null;
readonly isLocallyFocused: boolean;
readonly transition: TransitionState | null;
}
The navigator handle returned by useNav. Read getters are reactive; mutators commit immediately. push / replace are overloaded per route by whether params are required.
Register
interface Register {} // empty — users augment it
Augment this via declare module '@sigx/lynx-navigation' with routes: typeof routes to register your route map. This drives all global type inference.
RegisteredRoutes
type RegisteredRoutes = Register extends { routes: infer R } ? R : RouteMap;
Your registered route map, or the permissive RouteMap fallback when Register is not augmented.
RouteId
type RouteId = keyof RegisteredRoutes & string;
The union of registered route names.
RouteParams
type RouteParams<K extends RouteId> = ParamsOf<RegisteredRoutes[K]>;
The inferred params type for route K.
RouteSearch
type RouteSearch<K extends RouteId> = SearchOf<RegisteredRoutes[K]>;
The inferred search type for route K.
RoutesWithParams
type RoutesWithParams = { [K in RouteId]: RouteRequiresParams<RegisteredRoutes[K]> extends true ? K : never }[RouteId];
The union of route names that declare a params schema.
RoutesWithoutParams
type RoutesWithoutParams = Exclude<RouteId, RoutesWithParams>;
The union of route names with no params schema.
RouteDefinition
interface RouteDefinition<
Params extends StandardSchemaV1 | undefined = StandardSchemaV1 | undefined,
Search extends StandardSchemaV1 | undefined = StandardSchemaV1 | undefined,
> {
component: ComponentLike;
fallback?: ComponentLike | (() => unknown);
params?: Params;
search?: Search;
path?: string;
presentation?: Presentation;
children?: Record<string, RouteDefinition>;
}
A single route entry. params / search are Standard Schema validators; path enables deep links; presentation defaults to 'card'.
RouteMap
type RouteMap = Record<string, RouteDefinition>;
A map of route names to definitions.
ComponentLike
type ComponentLike = ((...args: any[]) => unknown) | (() => Promise<{ default: (...args: any[]) => unknown }>);
A route component — a function component or a dynamic import (for lazy routes).
Presentation
type Presentation = 'card' | 'modal' | 'fullScreen' | 'transparent-modal' | 'sheet';
How a screen is presented. card stays in its stack; the others escalate to root from nested stacks. sheet is a partial-height bottom sheet with a dimmed backdrop.
StackEntry
interface StackEntry<R extends string = string, P = unknown, S = unknown> {
readonly key: string;
readonly route: R;
readonly params: P;
readonly search: S;
state: unknown; // user state, survives suspend/restore
readonly presentation: Presentation;
}
One entry in a navigator stack.
PushOptions
interface PushOptions { presentation?: Presentation; state?: unknown; animated?: boolean; }
Per-push overrides (override the route's presentation, seed entry state, or disable animation for one push).
PopOptions
interface PopOptions { animated?: boolean; }
Per-pop options.
ScreenOptions
interface ScreenOptions {
title?: string | (() => string);
headerShown?: boolean; // default true
gestureEnabled?: boolean; // default true
detents?: DetentSpec[]; // sheet: rest heights (fraction / px / keyboard); default [{ fraction: 0.5 }]
initialDetentIndex?: number; // default: last (most open)
dragMode?: 'handle' | 'surface' | 'grabber'; // sheet: what owns the drag; renamed from dragHandle
backdropDismiss?: boolean; // default true
}
Per-screen options, settable via <Screen> or useScreenOptions. The detents / initialDetentIndex / backdropDismiss fields apply to sheet presentations. They use the DetentSpec geometry from @sigx/lynx-sheet — the route sheet now shares that engine, and the detents are tracked live. Renamed in 0.19 from snapPoints / initialSnapIndex (which took plain fraction numbers).
ScreenSlotFills
interface ScreenSlotFills {
header?: () => unknown;
headerLeft?: () => unknown;
headerRight?: () => unknown;
tabBarItem?: (ctx: { active: boolean }) => unknown;
}
The slot fills a screen can register (written by the Screen.* sub-components).
ScreenChrome
interface ScreenChrome {
readonly title: string;
readonly headerShown: boolean;
readonly canGoBack: boolean;
pop(): void;
readonly header: ScreenSlotFills['header'] | undefined;
readonly headerLeft: ScreenSlotFills['headerLeft'] | undefined;
readonly headerRight: ScreenSlotFills['headerRight'] | undefined;
}
The reactive header data returned by useScreenChrome. Every property is a getter.
Href
interface Href<K extends RouteId = RouteId> {
readonly route: K;
readonly params: RouteParams<K>;
readonly search: RouteSearch<K>;
readonly url: string | null; // null when the route has no path
}
A typed, validated navigation target produced by hrefFor / parseHref.
CompiledPath
interface CompiledPath {
readonly source: string;
readonly paramNames: readonly string[];
readonly regex: RegExp;
format(params: Record<string, string | number>): string;
}
The result of compilePath — a regex matcher plus a format() that fills the template.
NavSnapshot
interface NavSnapshot { version: number; stack: StackEntry[]; }
A persisted navigator snapshot (version plus stack).
NavStorageAdapter
interface NavStorageAdapter {
load(): Promise<NavSnapshot | null> | NavSnapshot | null;
save(snapshot: NavSnapshot): Promise<void> | void;
}
The storage backend for useNavSerializer. Implement load / save over any store (e.g. @sigx/lynx-storage).
UseNavSerializerOptions
interface UseNavSerializerOptions {
storage: NavStorageAdapter;
debounceMs?: number; // default 250
onRestored?: (snapshot: NavSnapshot) => void;
onRestoreError?: (reason: 'version' | 'shape' | 'unknown-route' | 'load-threw', err?: unknown) => void;
}
Options for useNavSerializer.
UseLinkingNavOptions
interface UseLinkingNavOptions {
prefixes?: string[];
onURL?: (url: string, nav: Nav) => void;
onUnmatched?: (url: string) => void;
replaceInitial?: boolean; // default true
}
Options for useLinkingNav.
TabInfo
interface TabInfo {
readonly name: string;
readonly icon?: IconSpec | JSXElement;
readonly label?: string;
readonly accessibilityLabel?: string;
}
Metadata for a single tab (from each Tabs.Screen).
TabsNav
interface TabsNav {
readonly active: string;
setActive(name: string): void;
readonly tabs: ReadonlyArray<TabInfo>;
}
The handle returned by useTabs.
TabRenderContext
interface TabRenderContext { readonly active: boolean; onPress(): void; }
The render context passed to TabBar's renderTab for each item.
DrawerNav
interface DrawerNav { readonly isOpen: boolean; open(): void; close(): void; toggle(): void; }
The handle returned by useDrawer.
LinkProps
type LinkProps = LinkPropsByRoute & { replace?: boolean; children?: unknown };
// LinkPropsByRoute = {
// [K in RouteId]: K extends RoutesWithParams
// ? { to: K; params: RouteParams<K>; search?: RouteSearch<K> }
// : { to: K; params?: undefined; search?: RouteSearch<K> }
// }[RouteId]
The props of <Link> — params is required exactly when the route declares a params schema.
TransitionState
interface TransitionState {
readonly kind: TransitionKind;
readonly topEntry: StackEntry;
readonly underneathEntry: StackEntry;
readonly progress: unknown; // SharedValue<number>, typed loosely
}
The in-flight transition exposed by nav.transition (null when idle).
TransitionKind
type TransitionKind = 'push' | 'pop';
The direction of a transition.
TransitionRole
type TransitionRole = 'top' | 'underneath';
A screen's role during a transition.
StandardSchemaV1
interface StandardSchemaV1<Input = unknown, Output = Input> {
readonly '~standard': {
readonly version: 1;
readonly vendor: string;
readonly types?: { readonly input: Input; readonly output: Output };
};
}
The Standard Schema interface used for params / search validators (zod, valibot, arktype all implement it).
InferOutput
type InferOutput<S> = S extends StandardSchemaV1<unknown, infer O> ? O : unknown;
Extracts the output type of a Standard Schema.
EmptyParams
type EmptyParams = Record<string, never>;
The params / search type for routes that declare no schema.
ParamsOf
type ParamsOf<R> = R extends { params: infer S } ? InferOutput<S> : EmptyParams;
Infers a route definition's params type.
SearchOf
type SearchOf<R> = R extends { search: infer S } ? InferOutput<S> : EmptyParams;
Infers a route definition's search type.
RouteRequiresParams
type RouteRequiresParams<R> = R extends { params: object } ? true : false;
Whether a route definition declares a params schema.
