Skip to content
Shiny.Maui.Shell v6 support for AI routing tools Learn More

Toast

A service-first toast notification system. Unlike other controls in the library, Toast is invoked entirely from code — no XAML placement or special base class required. Show lightweight, transient messages with auto-dismiss, manual dismiss via IDisposable, queue/stack behavior, spinner, progress bar, and full styling control.

  • NuGet downloads for Shiny.Maui.Controls
  • NuGet downloads for Shiny.Blazor.Controls
Frameworks
.NET MAUI
Blazor
  • Code-invoked — inject IToaster and call await toaster.ShowAsync("text") with no XAML setup required
  • IDisposable dismissShowAsync returns an IDisposable for programmatic dismiss
  • Auto-dismiss — configurable duration (default 3s), or TimeSpan.Zero for manual only
  • Pill or Fill — rounded pill (centered, offset from edges) or full-width bar (flush with edges)
  • Top or Bottom — position at top or bottom of screen
  • Queue or Stack — one-at-a-time queue (default) or multiple visible stacked toasts
  • Spinner — indeterminate loading indicator on left or right
  • Progress bar — countdown bar that drains over the duration
  • Icon — optional icon image
  • Tap actionICommand (MAUI) or Action (Blazor) on tap, with optional dismiss-on-tap
  • Feedback — tactile feedback on show/dismiss (MAUI)
  • AccessibilitySemanticScreenReader.Announce() on show (MAUI), role="alert" (Blazor)
  • Safe area aware — respects iOS home indicator and status bar
  • Styling — background color, text color, border, corner radius

IToaster is registered automatically by UseShinyControls(). Inject it via constructor injection. The toast overlay auto-attaches to the current page on first use — no XAML setup required.

using Shiny.Maui.Controls.Toast;
public class MyViewModel(IToaster toaster)
{
// Simple toast
await toaster.ShowAsync("Item saved!");
// With configuration
IDisposable toast = await toaster.ShowAsync("Uploading...", cfg =>
{
cfg.Spinner = ToastSpinnerPosition.Left;
cfg.Duration = TimeSpan.Zero; // manual dismiss only
cfg.DismissOnTap = false;
});
// Dismiss when done
toast.Dispose();
}
await toaster.ShowAsync("Connection lost", cfg =>
{
cfg.Duration = TimeSpan.FromSeconds(5);
cfg.Position = ToastPosition.Bottom;
cfg.DisplayMode = ToastDisplayMode.FillHorizontal;
cfg.DismissOnTap = true;
cfg.QueueMode = ToastQueueMode.Stack;
cfg.UseFeedback = true;
cfg.ShowProgressBar = true;
cfg.BackgroundColor = Colors.Red;
cfg.TextColor = Colors.White;
cfg.BorderColor = Colors.DarkRed;
cfg.BorderThickness = 1;
cfg.Icon = ImageSource.FromFile("warning.png");
cfg.TapCommand = new Command(() => NavigateToDetails());
});

Convenience methods with preset colors for common notification types:

await toaster.InfoAsync("Update available"); // Blue
await toaster.SuccessAsync("File saved"); // Green
await toaster.WarningAsync("Storage almost full"); // Amber
await toaster.DangerAsync("Save failed"); // Orange
await toaster.CriticalAsync("System error"); // Red
// Override after theme defaults are applied
await toaster.SuccessAsync("Done!", cfg =>
{
cfg.Duration = TimeSpan.FromSeconds(5);
cfg.ShowProgressBar = true;
});

Define ToastTypeStyle resources in App.xaml to override the built-in defaults:

<Application.Resources>
<shiny:ToastTypeStyle x:Key="ShinyToastSuccessStyle"
BackgroundColor="#065F46"
TextColor="White"
BorderColor="#10B981" />
<shiny:ToastTypeStyle x:Key="ShinyToastCriticalStyle"
BackgroundColor="#7F1D1D"
TextColor="White"
BorderColor="#DC2626"
BorderThickness="2" />
</Application.Resources>

Style keys: ShinyToastInfoStyle, ShinyToastSuccessStyle, ShinyToastWarningStyle, ShinyToastDangerStyle, ShinyToastCriticalStyle

Register the toast service in Program.cs:

using Shiny.Blazor.Controls.Toast;
builder.Services.AddShinyToast();

Place <ToastHost /> once in your layout (e.g., MainLayout.razor):

@using Shiny.Blazor.Controls.Toast
<div class="page">
<main>@Body</main>
</div>
<ToastHost />
@inject IToastService ToastService
<button @onclick="ShowToast">Save</button>
@code {
async Task ShowToast()
{
await ToastService.ShowAsync("Saved!", cfg =>
{
cfg.Position = ToastPosition.Bottom;
cfg.Duration = TimeSpan.FromSeconds(3);
});
}
}
await ToastService.InfoAsync("Update available");
await ToastService.SuccessAsync("File saved");
await ToastService.WarningAsync("Storage almost full");
await ToastService.DangerAsync("Save failed");
await ToastService.CriticalAsync("System error");
PropertyType (MAUI / Blazor)DefaultDescription
Textstring(required)Toast message text
DurationTimeSpan3sAuto-dismiss duration. TimeSpan.Zero = manual only
PositionToastPositionBottomTop or Bottom
DisplayModeToastDisplayModePillPill (rounded, offset) or FillHorizontal (flush, full width)
DismissOnTapbooltrueTap to dismiss
QueueModeToastQueueModeQueueQueue (one at a time) or Stack (multiple visible)
OffsetThickness / double12Margin from edges (pill mode only)
SpinnerToastSpinnerPositionNoneNone, Left, or Right
UseFeedbackbooltrueFeedback on show/dismiss (MAUI only)
ShowProgressBarboolfalseCountdown progress bar
BackgroundColorColor? / string?dark grayBackground fill
TextColorColor? / string?whiteText color
BorderColorColor? / string?noneBorder stroke
BorderThicknessdouble0Border width
CornerRadiusdouble20Corner radius (pill mode)
IconImageSource?nullOptional icon (MAUI)
IconHtmlstring?nullOptional HTML/SVG icon (Blazor)
TapCommandICommand?nullCommand on tap (MAUI)
TapCallbackAction?nullCallback on tap (Blazor)
AnnounceToScreenReaderbooltrueScreen reader announce (MAUI)
public partial class UploadViewModel(IToaster toaster) : ObservableObject
{
IDisposable? uploadToast;
[RelayCommand]
async Task Upload()
{
uploadToast = await toaster.ShowAsync("Uploading...", cfg =>
{
cfg.Spinner = ToastSpinnerPosition.Left;
cfg.Duration = TimeSpan.Zero;
});
await DoUploadAsync();
uploadToast.Dispose();
await toaster.ShowAsync("Upload complete!");
}
}
claude plugin marketplace add shinyorg/skills
claude plugin install shiny-controls@shiny
copilot plugin marketplace add https://github.com/shinyorg/skills
copilot plugin install shiny-controls@shiny
View shiny-controls Plugin