Skip to content
Introducing AI Conversations: Natural Language Interaction for Your Apps! Learn More

Shell Releases

Fix
IApplication resolved at initialization instead of constructor injectionShinyShellNavigator now resolves IApplication from the service provider during Initialize instead of requiring it via constructor injection. This fixes DI resolution failures when IApplication is not yet available at service registration time
Fix
AI extensions generation decoupled from nav extensionsAiExtensions.g.cs is now generated independently when ShinyMauiShell_GenerateAiExtensions is true and the Microsoft.Extensions.AI package is present, regardless of the ShinyMauiShell_GenerateNavExtensions setting
Fix
Routes without properties now generate valid NavigateToRoute code — AI navigation for routes with no [ShellProperty] attributes now correctly calls NavigateTo<TViewModel>() without an empty lambda, fixing a compile error in the generated code
Fix
MSBuild targets packaged under correct filenamePackage.targets is now packed as Shiny.Maui.Shell.targets instead of Shiny.Maui.Shell.SourceGenerators.targets, ensuring MSBuild properties like ShinyMauiShell_GenerateAiExtensions are correctly imported by consuming projects
Fix
Source generator analyzer DLL uses $(Configuration) — the packed analyzer DLL path now respects the active build configuration instead of being hardcoded to Release, fixing development-time source generation when building in Debug
Feature
SHINY004 warning — new diagnostic warns when [ShellProperty] attributes have descriptions but the parent [ShellMap] has no description. AI tools cannot determine when to navigate to a route without a route-level description
Feature
ShinyMauiShell_AiToolsClassName MSBuild property — customize the generated AiMauiShellTools class name via this new build property (default: AiMauiShellTools)
Fix
ShinyMauiShell_GenerateAiExtensions now defaults to disabled (opt-in) — previously defaulted to enabled, causing compile error SHINY003 when Microsoft.Extensions.AI was not referenced. Projects must now explicitly set ShinyMauiShell_GenerateAiExtensions to true to enable AI extensions
Feature
AiMauiShellTools class — AI tools and prompt are now encapsulated in a generated AiMauiShellTools class that takes INavigator via constructor injection. Provides Prompt (pre-formatted route descriptions) and Tools (AITool[]) properties, plus GetAiToolApplicableGeneratedRoutes() and NavigateToRoute() instance methods. Designed for DI registration as a singleton
Feature
AddAiTools() extension — new generated extension method on ShinyAppBuilder that registers AiMauiShellTools as a singleton: builder.UseShinyShell(x => x.AddGeneratedMaps().AddAiTools())
Feature
ShinyMauiShell_AiToolsClassName MSBuild property — customize the generated AI tools class name (default: AiMauiShellTools)
Enhancement
AI extensions supportShinyMauiShell_GenerateAiExtensions enables AI tool generation when set to true. Requires Microsoft.Extensions.AI package (SHINY003 error if missing)
BREAKING Enhancement
AI tools moved from extension methods to AiMauiShellTools classGetAiToolApplicableGeneratedRoutes(), NavigateToRoute(), AiRoutePrompt(), and GetAiTools() are no longer extension methods on INavigator. Instead, inject AiMauiShellTools and use its Prompt, Tools, and instance methods. GetGeneratedRouteInfo() remains as a static extension on INavigator

AI tools have moved from INavigator extension methods to the injectable AiMauiShellTools class:

// Before (v6.0) — extension methods on INavigator
var tools = navigator.GetAiTools();
var prompt = navigator.AiRoutePrompt();
var routes = navigator.GetAiToolApplicableGeneratedRoutes();
// After (v6.0.1) — inject AiMauiShellTools
// MauiProgram.cs
builder.UseShinyShell(x => x
.AddGeneratedMaps()
.AddAiTools() // registers AiMauiShellTools as singleton
);
// ViewModel
public class ChatViewModel(AiMauiShellTools aiTools)
{
var tools = aiTools.Tools;
var prompt = aiTools.Prompt;
var routes = aiTools.GetAiToolApplicableGeneratedRoutes();
}

AI extensions are now enabled by default. If you don’t use AI features and don’t have Microsoft.Extensions.AI installed, either install the package or explicitly disable:

<PropertyGroup>
<ShinyMauiShell_GenerateAiExtensions>false</ShinyMauiShell_GenerateAiExtensions>
</PropertyGroup>
Feature
AI-compatible source generation[ShellMap] now accepts a description parameter and [ShellProperty] now accepts description as the first parameter. When provided, the source generator emits XML doc comments, [System.ComponentModel.Description] attributes on methods and parameters, enabling AI tooling and IDE discoverability
Feature
GetGeneratedRouteInfo() extension method — new generated AiExtensions.g.cs produces a GetGeneratedRouteInfo() method on INavigator that returns an array of GeneratedRouteInfo records containing route names, descriptions, and parameter metadata. Designed for AI agents and tooling to discover available navigation routes at runtime
Feature
DisableShellFlyoutSwipeHandler — new opt-in custom handler that disables the Shell flyout swipe gesture while keeping the hamburger button functional. Call DisableShellFlyoutSwipeHandler.Register() in MauiProgram.cs to enable. Supported on Android (locks DrawerLayout), iOS/Mac Catalyst (disables UIPanGestureRecognizer), and no-op on Windows
BREAKING Enhancement
ShellPropertyAttribute parameter order changeddescription is now the first parameter (string? description = null, bool required = true), replacing the previous (bool required = true) signature. Positional usage like [ShellProperty(true)] must change to [ShellProperty(required: true)]
Enhancement
GeneratedRouteInfo record — new Shiny.Infrastructure.GeneratedRouteInfo and GeneratedRouteParameter records provide structured route metadata for runtime inspection
Enhancement
IQueryAttributable no longer required for [ShellProperty] — the source-generated navigation methods (NavigateTo{Name}() and AI NavigateToRoute()) now set [ShellProperty] properties directly on the ViewModel instance. IQueryAttributable is only needed if you use string-based NavigateTo(route, args) with tuple parameters

The ShellPropertyAttribute constructor parameter order changed. Update any positional bool arguments to use named syntax:

// Before (v5.x)
[ShellProperty(true)]
public string Name { get; set; }
[ShellProperty(false)]
public string OptionalNote { get; set; }
// After (v6.0)
[ShellProperty(required: true)]
public string Name { get; set; }
[ShellProperty(required: false)]
public string OptionalNote { get; set; }
// New: add descriptions for AI tooling
[ShellProperty("The user's display name")]
public string Name { get; set; }
[ShellProperty("Optional note text", required: false)]
public string OptionalNote { get; set; }

Update [ShellMap] to include descriptions:

// Before (v5.x)
[ShellMap<DetailPage>("Detail")]
// After (v6.0) — description is optional
[ShellMap<DetailPage>("Detail", description: "Navigate to the detail page")]
using Shiny.Handlers;
// In MauiProgram.cs, before builder.Build()
DisableShellFlyoutSwipeHandler.Register();
Feature
Tab badgesINavigator now supports SetTabBadge(string route, int value), SetTabBadge&lt;TViewModel&gt;(int value), ClearTabBadge(string route), and ClearTabBadge&lt;TViewModel&gt;(), enabling numeric badges on Shell tabs by route or ViewModel mapping
Feature
XAML navigation — new Navigate attached properties enable route-based navigation directly from XAML on Button, MenuItem, and ToolbarItem, including support for a single parameter pair or a NavigationParameters collection
Enhancement
Native badge support — tab badges are implemented natively for Android, iOS, Mac Catalyst, and Windows. Unsupported platforms such as Linux and macOS AppKit now throw PlatformNotSupportedException instead of silently doing nothing
Feature
UxDivers Dialogs — new Shiny.Maui.Shell.UxDiversDialogs package provides an alternative IDialogs implementation powered by UXDivers Popups. Drop-in replacement — no ViewModel changes needed, only the visual presentation changes
Feature
UseUxDiversDialogs() extension methods — two extension methods for setup: MauiAppBuilder.UseUxDiversDialogs() initializes the UxDivers popup infrastructure, and ShinyAppBuilder.UseUxDiversDialogs() registers the IDialogs implementation

Install the package and the UxDivers dependency:

Terminal window
dotnet add package UXDivers.Popups.Maui

Add theme dictionaries to App.xaml:

<uxd:DarkTheme xmlns:uxd="clr-namespace:UXDivers.Popups.Maui.Controls;assembly=UXDivers.Popups.Maui" />
<uxd:PopupStyles xmlns:uxd="clr-namespace:UXDivers.Popups.Maui.Controls;assembly=UXDivers.Popups.Maui" />

Configure in MauiProgram.cs:

builder
.UseMauiApp<App>()
.UseUxDiversDialogs() // Initialize UxDivers popup infrastructure
.UseShinyShell(x => x
.UseUxDiversDialogs() // Register as IDialogs provider
.AddGeneratedMaps()
)
IDialogs MethodUxDivers Popup Used
AlertSimpleActionPopup (single button)
ConfirmSimpleActionPopup (two buttons)
PromptFormPopup with single FormField
ActionSheetOptionSheetPopup with OptionSheetItem per button
BREAKING Enhancement
AddGeneratedMaps() is no longer generated when nav extensions are disabled — setting ShinyMauiShell_GenerateNavExtensions to false now also prevents generation of NavigationBuilderExtensions.g.cs (AddGeneratedMaps). A SHINY002 warning is emitted when [ShellMap] attributes are detected but nav extensions are disabled
Enhancement
AddGeneratedMaps() is no longer generated when no maps existNavigationBuilderExtensions.g.cs is now only produced when at least one [ShellMap] attribute is present, removing the empty stub that was previously always emitted
  • If you relied on AddGeneratedMaps() being available before adding any [ShellMap] attributes, add at least one [ShellMap] to generate the method
  • If you set ShinyMauiShell_GenerateNavExtensions to false but still used AddGeneratedMaps(), either remove the property or replace AddGeneratedMaps() with manual .Add<TPage, TViewModel>() calls
Feature
INavigationBuilder — fluent builder for multi-segment Shell navigation URIs. Chain Add<TViewModel>(), Add(string), and PopBack() calls, then execute with a single Navigate(). Supports relative, root (fromRoot: true), and mixed pop/push patterns like ../../Page1/Page2
Feature
INavigator.CreateBuilder(bool fromRoot) — factory method to create an INavigationBuilder instance. Pass fromRoot: true to build absolute URIs prefixed with //
Feature
Generated INavigationBuilder extensions — source generator now produces NavigationBuilderNavExtensions.g.cs with typed Add{Name}() methods for each [ShellMap] ViewModel, enabling fluent chains like .AddDetail(id: 42).AddModal().Navigate()
BREAKING Enhancement
relativeNavigation parameterNavigateTo(string) and NavigateTo<TViewModel>() now accept bool relativeNavigation = true. When false, the URI is prefixed with // for root navigation. Replaces the previous SetRoot<TViewModel>() method
Enhancement
Generated NavigationExtensions — all generated NavigateTo{Name}() methods now include a bool relativeNavigation = true parameter
  • SetRoot<TViewModel>() has been removed — use NavigateTo<TViewModel>(relativeNavigation: false) instead
  • Calls to NavigateTo that pass tuple args as positional parameters may need the args: named parameter to disambiguate from the new bool relativeNavigation parameter
// Before (v3.x)
await navigator.SetRoot<HomeViewModel>(vm => vm.Message = "Hello");
await navigator.NavigateTo("details", ("Id", 42));
// After (v4.0)
await navigator.NavigateTo<HomeViewModel>(vm => vm.Message = "Hello", relativeNavigation: false);
await navigator.NavigateTo("details", args: [("Id", 42)]);
// New: Navigation Builder
await navigator
.CreateBuilder()
.AddDetail(id: 42)
.AddModal()
.Navigate();
// New: Pop back and push
await navigator
.CreateBuilder()
.PopBack(2)
.AddHome()
.Navigate();
Fix
MainThread dispatch on macOS & LinuxMauiMainThread now bypasses MainThread.InvokeOnMainThreadAsync on macOS and Linux, where MAUI’s implementation fails or deadlocks. INavigator and IDialogs dispatch calls now work reliably on both platforms
Feature
IMainThread interface — thread-marshalling abstraction used internally by ShellNavigator and ShellDialogs, now registered as a singleton so you can inject it directly instead of using Microsoft.Maui.ApplicationModel.MainThread
Feature
ShellServices record — convenience aggregate of INavigator, IDialogs, and IMainThread — inject a single parameter when a ViewModel needs most of them
Feature
UseDialogs<TDialog>() — plug in a custom IDialogs implementation via UseShinyShell(x => x.UseDialogs<MyDialogs>()). The default ShellDialogs registration now uses TryAddSingleton so user overrides always win
Feature
ShinyShell base class — new ShinyShell class that overrides OnNavigated to deterministically set the initial page’s BindingContext via Shell’s own lifecycle, eliminating a race condition where the Application.PageAppearing event could fire before the handler was registered
Fix
BindingContext inheritance fix — BindingContext checks now correctly detect inherited values (e.g., the Shell instance propagated down the visual tree) instead of only checking for null, ensuring ViewModels are always assigned to their mapped pages

Your AppShell (and any other Shell subclass) must now inherit from ShinyShell instead of Shell:

AppShell.xaml — change root element:

<shiny:ShinyShell
x:Class="MyApp.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:shiny="clr-namespace:Shiny;assembly=Shiny.Maui.Shell"
xmlns:local="clr-namespace:MyApp"
Title="MyApp">
<!-- ... -->
</shiny:ShinyShell>

AppShell.xaml.cs — change base class:

using Shiny;
public partial class AppShell : ShinyShell
{
public AppShell()
{
InitializeComponent();
}
}
Fix
INavigationConfirmation no longer triggers during programmatic navigation — CanNavigate() is now only called for user-initiated navigation (back button, tab switches), not when navigating via INavigator methods
Feature
SwitchShell — swap the entire active Shell at runtime via INavigator.SwitchShell(shell) or INavigator.SwitchShell<TShell>() (DI-resolved). Fires Navigating/Navigated events with NavigationType.SwitchShell and respects INavigationAware.OnNavigatingFrom
Feature
SwitchShell<TShell>() — generic overload resolves the Shell from the DI container, enabling constructor-injected Shell instances
Feature
NavigationType.SwitchShell — new enum value for tracking shell switches in navigation events and analytics
Feature
IDialogs interface — new dedicated dialog service with Alert, Confirm, Prompt, and ActionSheet methods, injected separately from INavigator for clean separation of concerns
Feature
Prompt dialog — display a text input dialog with customizable keyboard type, placeholder, initial value, and max length
Feature
ActionSheet dialog — display a multi-option action sheet with cancel and destructive action support
BREAKING Enhancement
Alert and Confirm moved from INavigator to IDialogs — improves testability and follows interface segregation
  • INavigator.Alert() and INavigator.Confirm() have been removed — inject IDialogs instead
  • Replace navigator.Alert(...) with dialogs.Alert(...) and navigator.Confirm(...) with dialogs.Confirm(...)
  • Add IDialogs to your ViewModel constructor parameters where dialogs are used
  • IDialogs is automatically registered by UseShinyShell() — no additional setup required
// Before (v2.x)
public class MyViewModel(INavigator navigator)
{
await navigator.Alert("Error", "Something went wrong");
bool ok = await navigator.Confirm("Delete?", "Are you sure?");
}
// After (v3.0)
public class MyViewModel(INavigator navigator, IDialogs dialogs)
{
await dialogs.Alert("Error", "Something went wrong");
bool ok = await dialogs.Confirm("Delete?", "Are you sure?");
// New capabilities
var name = await dialogs.Prompt("Name", "Enter your name");
var choice = await dialogs.ActionSheet("Options", "Cancel", null, "Edit", "Share");
}
Feature
Configurable source generation — disable route constants via ShinyMauiShell_GenerateRouteConstants or navigation extensions via ShinyMauiShell_GenerateNavExtensions MSBuild properties (empty/missing = enabled, false = disabled)
Feature
Route-based naming — the route parameter in [ShellMap] now drives the generated constant name and navigation method name (e.g., [ShellMap<HomePage>("Dashboard")]Routes.Dashboard, NavigateToDashboard)
Feature
Invalid route diagnostic — SHINY001 compiler error when the route value is not a valid C# identifier (hyphens, spaces, leading digits)
Enhancement
AddGeneratedMaps() was always generated — even before any [ShellMap] attributes existed — so you could wire up MauiProgram.cs immediately (changed in v4.1: now requires at least one [ShellMap])
Enhancement
AddGeneratedMaps() now uses inline string literals instead of Routes.* constants, so it works correctly even when route constant generation is disabled
Enhancement
When no route is specified, the generated name falls back to the page type name without the Page suffix (e.g., [ShellMap<HomePage>]Routes.Home)
  • Route constant names may change if you specified explicit routes — e.g., Routes.Home for [ShellMap<HomePage>("Dashboard")] is now Routes.Dashboard
  • Navigation extension method names change similarly — NavigateToHome becomes NavigateToDashboard
  • Routes with invalid C# identifiers (hyphens, spaces, leading digits) now produce compile errors — rename them to valid identifiers