Shell | Navigation
All navigation in Shiny Shell goes through the INavigator interface. Inject it into your ViewModels via constructor injection.
public class MyViewModel(INavigator navigator){ // navigator is ready to use}NavigateTo (Route)
Section titled “NavigateTo (Route)”Navigate to a registered route by name, optionally passing arguments as key-value tuples.
// Simple route navigationawait navigator.NavigateTo("details");
// With argumentsawait navigator.NavigateTo("details", ("Id", 42), ("Name", "Allan"));Arguments are received on the target ViewModel via IQueryAttributable.ApplyQueryAttributes.
NavigateTo (ViewModel)
Section titled “NavigateTo (ViewModel)”Navigate by ViewModel type with an optional configuration action for strongly-typed argument passing.
// Navigate to a ViewModelawait navigator.NavigateTo<DetailViewModel>();
// With strongly-typed property setupawait navigator.NavigateTo<DetailViewModel>(vm => vm.Id = 42);
// With additional route argumentsawait navigator.NavigateTo<DetailViewModel>( vm => vm.Id = 42, ("ExtraArg", "value"));Relative Navigation
Section titled “Relative Navigation”Both NavigateTo overloads accept a relativeNavigation parameter (default true). When set to false, the URI is prefixed with // to navigate from the root, replacing the navigation stack.
// Relative navigation (default) — pushes onto the stackawait navigator.NavigateTo("details");await navigator.NavigateTo<DetailViewModel>();
// Root navigation — resets to root and navigatesawait navigator.NavigateTo("dashboard", relativeNavigation: false);await navigator.NavigateTo<DashboardViewModel>(relativeNavigation: false);
// With configurationawait navigator.NavigateTo<DashboardViewModel>( vm => vm.WelcomeMessage = "Hello!", relativeNavigation: false);Navigation Builder
Section titled “Navigation Builder”The INavigationBuilder provides a fluent API for constructing multi-segment navigation URIs in a single call. Create one via INavigator.CreateBuilder().
// Push three pages in one navigation: ChainPage/DetailPage/ChainPageawait navigator .CreateBuilder() .Add<ChainViewModel>(x => x.Text = "Page1") .Add<DetailViewModel>(x => x.Id = 42) .Add<ChainViewModel>(x => x.Text = "Page3") .Navigate();
// Pop back 2 pages, then push a new page: ../../ChainPageawait navigator .CreateBuilder() .PopBack(2) .Add<ChainViewModel>(x => x.Text = "After Pop") .Navigate();
// Navigate from root: //ChainPage/DetailPageawait navigator .CreateBuilder(fromRoot: true) .Add<ChainViewModel>(x => x.Text = "Root Start") .Add<DetailViewModel>(x => x.Id = 1) .Navigate();
// Mix string routes and ViewModel-based segmentsawait navigator .CreateBuilder() .Add("settings") .Add<DetailViewModel>(x => x.Id = 99) .Navigate();| Method | Description |
|---|---|
CreateBuilder(bool fromRoot) | Creates a builder. fromRoot: true prefixes the URI with // |
PopBack(int count) | Adds .. pop segments. Must be called before any Add calls. Not valid with fromRoot: true |
Add<TViewModel>() | Adds a route segment for the ViewModel type |
Add<TViewModel>(configure) | Adds a route segment with a configure callback invoked when the page is created |
Add(string routeName) | Adds a raw route string segment |
Navigate() | Builds the URI and executes the navigation |
GoBack
Section titled “GoBack”Navigate back one or more pages, optionally passing arguments to the previous ViewModel.
// Simple go backawait navigator.GoBack();
// Go back with argumentsawait navigator.GoBack(("Result", "saved"), ("Timestamp", DateTime.UtcNow));
// Go back multiple pagesawait navigator.GoBack(2);
// Go back multiple pages with argumentsawait navigator.GoBack(2, ("Result", "saved"));PopToRoot
Section titled “PopToRoot”Pop the entire navigation stack back to the root page.
await navigator.PopToRoot();
// With argumentsawait navigator.PopToRoot(("Status", "complete"));SwitchShell
Section titled “SwitchShell”Replace the entire active Shell at runtime. Useful for switching between different app experiences (e.g. tabbed layout, flyout menu, onboarding flow).
// Switch to a new Shell instanceawait navigator.SwitchShell(new AdminShell());
// Switch to a Shell resolved from DIawait navigator.SwitchShell<AdminShell>();The current Shell’s ViewModel receives OnNavigatingFrom before the switch, and the Navigating event fires with NavigationType.SwitchShell.
Navigation Events
Section titled “Navigation Events”INavigator exposes two events for observing navigation lifecycle:
Navigating (Pre-Navigation)
Section titled “Navigating (Pre-Navigation)”Fires before navigation occurs. Provides the source ViewModel instance and destination route.
navigator.Navigating += (sender, args) =>{ // args.FromUri — current location URI // args.FromViewModel — source ViewModel instance (object?) // args.ToUri — destination route URI // args.NavigationType — Push, SetRoot, GoBack, PopToRoot, or SwitchShell // args.Parameters — navigation parameters};| Property | Type | Description |
|---|---|---|
FromUri | string? | The current Shell location URI |
FromViewModel | object? | The source page’s ViewModel instance |
ToUri | string | The destination route URI |
NavigationType | NavigationType | Push, SetRoot, GoBack, PopToRoot, or SwitchShell |
Parameters | IReadOnlyDictionary<string, object> | Navigation parameters |
Navigated (Post-Navigation)
Section titled “Navigated (Post-Navigation)”Fires after navigation completes and the destination page’s ViewModel is resolved.
navigator.Navigated += (sender, args) =>{ // args.ToUri — destination route URI // args.ToViewModel — destination ViewModel instance (object?) // args.NavigationType — Push, SetRoot, GoBack, PopToRoot, or SwitchShell // args.Parameters — navigation parameters};| Property | Type | Description |
|---|---|---|
ToUri | string | The destination route URI |
ToViewModel | object? | The destination page’s ViewModel instance |
NavigationType | NavigationType | Push, SetRoot, GoBack, PopToRoot, or SwitchShell |
Parameters | IReadOnlyDictionary<string, object> | Navigation parameters |
Example: Navigation Logger
Section titled “Example: Navigation Logger”Use IMauiInitializeService to hook events for cross-cutting concerns like logging or analytics:
public class NavigationLogger( ILogger<NavigationLogger> logger, INavigator navigator) : IMauiInitializeService{ public void Initialize(IServiceProvider services) { navigator.Navigating += (_, args) => { logger.LogInformation( "Navigating from '{FromUri}' to '{ToUri}' | Type: {NavigationType} | FromViewModel: {FromViewModel}", args.FromUri, args.ToUri, args.NavigationType, args.FromViewModel?.GetType().Name ); };
navigator.Navigated += (_, args) => { logger.LogInformation( "Navigated to '{ToUri}' | Type: {NavigationType} | ToViewModel: {ToViewModel}", args.ToUri, args.NavigationType, args.ToViewModel?.GetType().Name ); }; }}
// Register in MauiProgram.csbuilder.Services.AddSingleton<IMauiInitializeService, NavigationLogger>();Passing Arguments
Section titled “Passing Arguments”Arguments passed during navigation are delivered via the MAUI built-in IQueryAttributable interface.
public class DetailViewModel : IQueryAttributable{ public int Id { get; set; }
public void ApplyQueryAttributes(IDictionary<string, object> query) { if (query.TryGetValue("Id", out var id)) Id = (int)id; }}This works for arguments passed via NavigateTo, GoBack, and PopToRoot.