Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

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

MAUI

Toast Pill Toast Full Width

Blazor

Pill toast Themed toast
Pill toast on Blazor Themed toast on 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
  • Text overflow — ellipsis (truncate), multi-line (wrap), or marquee (scrolling ticker) with configurable speed
  • 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();

Or builder.Services.AddShinyControls() — one call registers this alongside every other service-backed control. Both are TryAdd, so calling either or both is safe.

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");
Property Type (MAUI / Blazor) Default Description
Text string (required) Toast message text
Duration TimeSpan 3s Auto-dismiss duration. TimeSpan.Zero = manual only
Position ToastPosition Bottom Top or Bottom
DisplayMode ToastDisplayMode Pill Pill (rounded, offset) or FillHorizontal (flush, full width)
DismissOnTap bool true Tap to dismiss
QueueMode ToastQueueMode Queue Queue (one at a time) or Stack (multiple visible)
Offset Thickness / double 12 Margin from edges (pill mode only)
Spinner ToastSpinnerPosition None None, Left, or Right
UseFeedback bool true Feedback on show/dismiss (MAUI only)
ShowProgressBar bool false Countdown progress bar
BackgroundColor Color? / string? dark gray Background fill
TextColor Color? / string? white Text color
BorderColor Color? / string? none Border stroke
BorderThickness double 0 Border width
CornerRadius double 20 Corner radius (pill mode)
Icon ImageSource? null Optional icon (MAUI)
IconHtml string? null Optional HTML/SVG icon (Blazor)
TapCommand ICommand? null Command on tap (MAUI)
TapCallback Action? null Callback on tap (Blazor)
TextOverflow ToastTextOverflow Ellipsis Ellipsis (truncate with …), MultiLine (word wrap), or Marquee (scrolling ticker)
MarqueeSpeedPixelsPerSecond double 40 Scroll speed for marquee mode (pixels per second)
AnnounceToScreenReader bool true Screen reader announce (MAUI)

Controls how long text is displayed when it exceeds the available toast width:

// Ellipsis (default) — truncates with "..."
await toaster.ShowAsync("Very long message...", cfg =>
{
cfg.TextOverflow = ToastTextOverflow.Ellipsis;
});
// Multi-line — wraps text to multiple lines
await toaster.ShowAsync("Very long message that will wrap to the next line", cfg =>
{
cfg.TextOverflow = ToastTextOverflow.MultiLine;
});
// Marquee — scrolling ticker animation
await toaster.ShowAsync("Very long message that scrolls continuously", cfg =>
{
cfg.TextOverflow = ToastTextOverflow.Marquee;
cfg.MarqueeSpeedPixelsPerSecond = 80; // faster scroll (default: 40)
cfg.Duration = TimeSpan.FromSeconds(10); // give time to read
});

Works identically in both MAUI and Blazor. Marquee speed is controlled via MarqueeSpeedPixelsPerSecond (default 40). Increase Duration for marquee toasts to give users time to read the scrolling content.

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@shiny

One plugin installs all 35 Shiny skills. Your agent loads only the skill relevant to what you're building, so there's no cost to having them all available.

copilot plugin marketplace add https://github.com/shinyorg/skills
copilot plugin install shiny@shiny

One plugin installs all 35 Shiny skills. Your agent loads only the skill relevant to what you're building, so there's no cost to having them all available.

View shiny-controls Skill