| name | reactor-navigation |
|---|---|
| description | Reactor's navigation system — UseNavigation hook, NavigationHost renderer, NavigationView sidebar, TabView tabs, route params, stack operations, page lifecycle, deep linking, page transitions, caching, and state serialization. Load this when building multi-page apps, wiring sidebar or tab navigation, or implementing deep links. |
Reactor uses a stack-based navigation model with type-safe routes. You
define routes as an enum, create a navigation handle with UseNavigation,
and render the current page with NavigationHost. The system manages
back/forward stacks, transitions, caching, and deep linking.
| API | Purpose |
|---|---|
UseNavigation(Route.Home) |
Create a NavigationHandle<Route> (call once at root) |
UseNavigation<Route>() |
Retrieve the nearest ancestor's handle (child components) |
NavigationHost(nav, route => ...) |
Render the current page |
NavigationView([items], content) |
Sidebar navigation with icons |
TabView(Tab(...), Tab(...)) |
Tab-based parallel navigation |
UseNavigationLifecycle(...) |
Page appear/disappear callbacks |
UseSystemBackButton(nav, window) |
Wire system back button to nav stack |
DeepLinkMap<Route> |
Map URI patterns to routes |
Use an enum. Each value is a distinct page:
enum Route { Home, Settings, Profile, Details }For routes that carry data, use a discriminated-union pattern with records:
abstract record Route
{
public record Home : Route;
public record Settings : Route;
public record Details(int ItemId) : Route;
}class App : Component
{
public override Element Render()
{
var nav = UseNavigation(Route.Home);
return VStack(12,
HStack(8,
Button("Home", () => nav.Navigate(Route.Home)),
Button("Settings", () => nav.Navigate(Route.Settings)),
Button("Back", () => nav.GoBack()).IsEnabled(nav.CanGoBack)
),
NavigationHost(nav, route => route switch
{
Route.Home => Component<HomePage>(),
Route.Settings => Component<SettingsPage>(),
_ => TextBlock("Not found")
})
);
}
}Key points:
UseNavigation(Route.Home)— call once at the root with the initial route.nav.Navigate(route)— pushes onto the back stack.nav.GoBack()/nav.GoForward()— stack navigation.NavigationHost(nav, route => ...)— renders the matched element.
| Method | Effect |
|---|---|
Navigate(route) |
Push route onto back stack |
GoBack() |
Pop current, return to previous |
GoForward() |
Move forward (after going back) |
Replace(route) |
Swap current route without touching the stack |
Reset(route) |
Clear all stacks, start fresh |
PopTo(predicate) |
Pop until a matching route is found |
Properties: CurrentRoute, CanGoBack, CanGoForward, BackStack,
ForwardStack, Depth.
Use Reset for sign-out flows to prevent navigating back to authenticated
pages.
var nav = UseNavigation(Route.Home);
return NavigationView(
[
NavItem("Home", icon: "Home", tag: "Home"),
NavItem("Settings", icon: "Setting", tag: "Settings"),
NavItem("Profile", icon: "Contact", tag: "Profile")
],
content: NavigationHost(nav, route => route switch
{
Route.Home => VStack(Heading("Home"), TextBlock("Welcome.")).Padding(24),
Route.Settings => VStack(Heading("Settings"), TextBlock("Configure.")).Padding(24),
Route.Profile => VStack(Heading("Profile"), TextBlock("Your info.")).Padding(24),
_ => TextBlock("Not found").Padding(24)
})
);NavItem(label, icon?, tag?) creates sidebar items. Icons are Segoe Fluent
Icons names. The NavigationView handles selection state.
Unlike stack navigation, tabs keep all content alive simultaneously:
return TabView(
Tab("Documents", VStack(12,
TextBlock("Your documents."),
Button("New", () => { })).Padding(24)),
Tab("Recent", TextBlock("Recently opened.").Padding(24)),
Tab("Shared", TextBlock("Shared files.").Padding(24))
);Use tabs when users switch freely between parallel workspaces.
UseNavigationLifecycle fires callbacks when a page appears or disappears:
UseNavigationLifecycle(
onNavigatedTo: ctx => LoadData(ctx.PreviousRoute),
onNavigatingFrom: ctx =>
{
if (hasUnsavedChanges) ctx.Cancel(); // prevent navigation
},
onNavigatedFrom: ctx => SaveDraft()
);| Callback | When |
|---|---|
onNavigatedTo |
After this page becomes active — fetch data, start timers |
onNavigatingFrom |
Before leaving — call ctx.Cancel() to block navigation |
onNavigatedFrom |
After leaving — save drafts, stop timers |
Set Transition on NavigationHost:
NavigationHost(nav, route => ...) with
{
Transition = NavigationTransition.DrillIn()
}| Transition | Effect |
|---|---|
NavigationTransition.Slide() |
Slide + fade (default) |
NavigationTransition.Fade() |
Crossfade |
NavigationTransition.DrillIn() |
Scale + fade — use for list→detail |
NavigationTransition.Spring() |
Spring-physics slide |
NavigationTransition.None |
Instant swap |
GoBack automatically reverses direction. Transitions run on the compositor thread.
DeepLinkMap maps URI patterns to route constructors:
var map = UseMemo(() => new DeepLinkMap<Route>()
.Map("/", _ => Route.Home)
.Map("/settings", _ => Route.Settings)
.Map("/users/{id:int}/posts/{postId:int}", args =>
{
var userId = args.Get<int>("id");
var postId = args.Get<int>("postId");
return Route.Details;
}, backStackFactory: () => new[] { Route.Home }));
// Resolve from activation URI:
map.Resolve(uri);Pattern segments:
| Segment | Matches |
|---|---|
/literal |
Exact match |
/{param} |
String capture |
/{param:int} |
Typed capture (int, long, bool, Guid) |
/{param?} |
Optional parameter |
/{**} |
Wildcard — remaining path |
?key=value |
Query string parameters |
backStackFactory builds a synthetic back stack so GoBack works after
deep-linked entry.
NavigationHost(nav, route => ...) with
{
CacheMode = NavigationCacheMode.Enabled,
CacheSize = 5
}| Cache mode | Behavior |
|---|---|
Disabled |
Always unmount/remount (default) |
Enabled |
LRU cache up to CacheSize entries |
Required |
Always cached, never evicted |
Caching preserves scroll position, text input, and component state.
var state = nav.GetState(); // NavigationState<TRoute> snapshot
nav.SetState(state); // restore stacks + fire Navigated(Reset)Reactor returns a plain NavigationState<TRoute> POCO and lets you pick the
storage format. For JSON persistence pair the snapshot with a
JsonSerializerContext (AOT-safe):
[JsonSerializable(typeof(NavigationState<AppRoute>))]
partial class AppJsonContext : JsonSerializerContext { }
var json = JsonSerializer.Serialize(nav.GetState(), AppJsonContext.Default.NavigationStateAppRoute);
// later…
nav.SetState(JsonSerializer.Deserialize(json, AppJsonContext.Default.NavigationStateAppRoute)!);Use for persisting navigation across app restarts.
UseSystemBackButton(nav, window);Wires the Windows title-bar back button and hardware back button to your navigation stack automatically.
- Call
UseNavigation(initial)once at the root. Children retrieve the same handle viaUseNavigation<Route>()(no initial value) through context. - Use enums for routes. Compile-time safety — can't navigate to a non-existent route.
- Use
Resetfor sign-out. Clears the stack so users can't go back. - DrillIn for list→detail. Signals hierarchy; pairs with connected animation.
ctx.Cancel()inonNavigatingFrom— the only way to guard against unsaved-changes navigation.- Don't forget
backStackFactoryin deep links. Without it, GoBack does nothing after a deep-linked entry.