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

Shell | Source Generation

Shiny Shell includes source generators that eliminate route registration boilerplate and create strongly-typed navigation extensions. Decorate your ViewModels with attributes and the generators handle the rest.

Apply [ShellMap<TPage>] to a ViewModel to register it with a page and route.

[ShellMap<DetailPage>("Detail")]
public class DetailViewModel
{
}
ParameterTypeDescription
routestringThe Shell route name for this page. Also used as the generated constant and method name. Must be a valid C# identifier.
registerRouteboolWhether to register the route with Shell (default: true). Set to false for pages already defined in AppShell.xaml
descriptionstringOptional description used by the source generator to produce XML doc comments, [Description] attributes, and GeneratedRouteInfo metadata. Enables AI tooling integration.
// Root page defined in AppShell.xaml — don't re-register the route
[ShellMap<MainPage>(registerRoute: false)]
public class MainViewModel
{
}

When you specify a route, it is used as:

  • The constant name in Routes (e.g., Routes.Detail)
  • The method suffix in navigation extensions (e.g., NavigateToDetail)
  • The string value passed to Shell routing

When no route is specified, the page type name without the Page suffix is used as the generated name, and the full page type name is used as the route value:

[ShellMap<DetailPage>("Detail")]
// → Routes.Detail = "Detail"
// → NavigateToDetail(...)
[ShellMap<HomePage>]
// → Routes.Home = "HomePage"
// → NavigateToHome(...)

Mark ViewModel properties with [ShellProperty] to include them in the generated navigation extension methods.

ParameterTypeDescription
descriptionstringOptional description used by the source generator to produce XML doc comments, [Description] attributes on parameters, and GeneratedRouteParameter metadata
requiredboolWhether this parameter is required for navigation (default: true). Optional parameters get default values in the generated method signature
[ShellMap<DetailPage>("Detail", description: "Navigate to the detail page")]
public class DetailViewModel
{
[ShellProperty("The item identifier")]
public int Id { get; set; }
[ShellProperty("The display name")]
public string Name { get; set; }
}

The generator creates a strongly-typed extension method with parameters matching each [ShellProperty], including XML docs and [Description] attributes when descriptions are provided:

// Generated — you call this instead of manual NavigateTo
await navigator.NavigateToDetail(id: 42, name: "Allan");

The source generators produce up to five files (nav extensions can be individually disabled):

A static class with const string fields for every route.

public static class Routes
{
public const string Detail = "Detail";
public const string Main = "MainPage";
public const string Modal = "Modal";
}

Strongly-typed extension methods on INavigator for each ViewModel with [ShellProperty] attributes. When description is set on [ShellMap] and [ShellProperty], the generated methods include XML doc comments and [Description] attributes for AI tooling integration.

public static class NavigationExtensions
{
/// <summary>
/// Navigate to the detail page
/// </summary>
/// <param name="id">The item identifier</param>
/// <param name="name">The display name</param>
/// <param name="relativeNavigation">If true, it will navigate/stack from where the application currently is otherwise, it will reset the stack to this new route</param>
[Description("Navigate to the detail page")]
public static Task NavigateToDetail(this INavigator navigator,
[Description("The item identifier")] int id,
[Description("The display name")] string name,
[Description("...")] bool relativeNavigation = true)
{
return navigator.NavigateTo<DetailViewModel>(x =>
{
x.Id = id;
x.Name = name;
}, relativeNavigation);
}
}

A DI registration method that replaces all manual .Add<TPage, TViewModel>() calls. Uses inline string literals so it works regardless of whether route constants are enabled.

public static class NavigationBuilderExtensions
{
public static ShinyAppBuilder AddGeneratedMaps(this ShinyAppBuilder builder)
{
builder.Add<DetailPage, DetailViewModel>("Detail");
builder.Add<MainPage, MainViewModel>("MainPage", registerRoute: false);
builder.Add<ModalPage, ModalViewModel>("Modal");
return builder;
}
}

Strongly-typed Add extension methods on INavigationBuilder for use with the fluent navigation builder. Generated alongside NavigationExtensions.g.cs when nav extensions are enabled.

public static class NavigationBuilderNavExtensions
{
public static INavigationBuilder AddDetail(this INavigationBuilder builder, int id, string name)
{
return builder.Add<DetailViewModel>(x => { x.Id = id; x.Name = name; });
}
public static INavigationBuilder AddModal(this INavigationBuilder builder)
{
return builder.Add<ModalViewModel>();
}
}

Usage:

await navigator
.CreateBuilder()
.AddDetail(id: 42, name: "Allan")
.AddModal()
.Navigate();

A GetGeneratedRouteInfo() extension method on INavigator that returns structured metadata about all registered routes and their parameters. Designed for AI agents and tooling to discover available navigation routes at runtime.

public static class AiExtensions
{
[Description("This provides a list of routes throughout the application")]
public static GeneratedRouteInfo[] GetGeneratedRouteInfo(this INavigator navigator) =>
[
new("Detail", "Navigate to the detail page",
[new("Id", "The item identifier", "int", true), new("Name", "The display name", "string", true)]),
new("MainPage", "", []),
new("Modal", "", [])
];
}

The GeneratedRouteInfo record contains:

  • Route — the Shell route name
  • Description — the description from [ShellMap] (empty string if not set)
  • Parameters — array of GeneratedRouteParameter records for all [ShellProperty] properties

Each GeneratedRouteParameter contains:

  • ParameterName — the property name
  • Description — from [ShellProperty("...")] (empty string if not set)
  • TypeName — the CLR type name (e.g., "string", "int")
  • IsRequired — from [ShellProperty(required: ...)]

All [ShellProperty] properties are included in the parameters array, even those without descriptions.

When AI extensions are enabled (ShinyMauiShell_GenerateAiExtensions=true), the generator also produces GetAiToolApplicableGeneratedRoutes(), NavigateToRoute(), GetAiTools(), and AiRoutePrompt for AI integration. NavigateToRoute automatically converts string values from the AI to the target property type — int, bool, double, enums (case-insensitive), DateTime, Guid, and more. See AI Integration for details.

You can disable individual generated files via MSBuild properties in your .csproj:

<PropertyGroup>
<!-- Disable route constants (Routes.g.cs) -->
<ShinyMauiShell_GenerateRouteConstants>false</ShinyMauiShell_GenerateRouteConstants>
<!-- Disable navigation extensions (NavigationExtensions.g.cs, NavigationBuilderNavExtensions.g.cs, and AddGeneratedMaps) -->
<ShinyMauiShell_GenerateNavExtensions>false</ShinyMauiShell_GenerateNavExtensions>
<!-- Enable AI extensions (disabled by default, requires Microsoft.Extensions.AI) -->
<ShinyMauiShell_GenerateAiExtensions>true</ShinyMauiShell_GenerateAiExtensions>
<!-- Customize the generated class name (default: AiExtensions) -->
<ShinyMauiShell_AiExtensionsClassName>MyAppRouteExtensions</ShinyMauiShell_AiExtensionsClassName>
<!-- Customize the AI navigate method name (default: NavigateToRoute) -->
<ShinyMauiShell_AiNavigateMethodName>GoToPage</ShinyMauiShell_AiNavigateMethodName>
</PropertyGroup>
PropertyDefaultControls
ShinyMauiShell_GenerateRouteConstantstrueRoutes.g.cs
ShinyMauiShell_GenerateNavExtensionstrueNavigationExtensions.g.cs, NavigationBuilderNavExtensions.g.cs, AiExtensions.g.cs, and NavigationBuilderExtensions.g.cs (AddGeneratedMaps)
ShinyMauiShell_GenerateAiExtensionsfalseGetAiToolApplicableGeneratedRoutes, NavigateToRoute, GetAiTools(), and AiRoutePrompt. Requires Microsoft.Extensions.AI (SHINY003 error if missing)
ShinyMauiShell_AiExtensionsClassNameAiExtensionsClass name for the route info/AI extensions class
ShinyMauiShell_AiNavigateMethodNameNavigateToRouteMethod name for the AI-friendly navigate method

NavigationBuilderExtensions.g.cs (AddGeneratedMaps()) and AiExtensions.g.cs are only generated when at least one [ShellMap] attribute is detected and ShinyMauiShell_GenerateNavExtensions is enabled. If maps are detected but nav extensions are disabled, the generator emits a SHINY002 warning.

// MauiProgram.cs — manual registration
builder
.UseShinyShell(x => x
.Add<MainPage, MainViewModel>(registerRoute: false)
.Add<DetailPage, DetailViewModel>("Detail")
.Add<ModalPage, ModalViewModel>("Modal")
);
// Navigation — string-based, error-prone
await navigator.NavigateTo("Detail", ("Id", 42), ("Name", "Allan"));
// MauiProgram.cs — one line
builder.UseShinyShell(x => x.AddGeneratedMaps());
// Navigation — strongly typed, compile-time checked
await navigator.NavigateToDetail(id: 42, name: "Allan");

Replace your manual route registrations with the generated method in MauiProgram.cs:

public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseShinyShell(x => x.AddGeneratedMaps())
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
});
return builder.Build();
}

All [ShellMap] decorated ViewModels are automatically registered — no manual .Add() calls needed.