Skip to content
Introducing AI Conversations: Natural Language Interaction for Your Apps! 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
Toast PillToast Full Width
  • 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();

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)
TextOverflowToastTextOverflowEllipsisEllipsis (truncate with …), MultiLine (word wrap), or Marquee (scrolling ticker)
MarqueeSpeedPixelsPerSeconddouble40Scroll speed for marquee mode (pixels per second)
AnnounceToScreenReaderbooltrueScreen 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 controls@shiny
copilot plugin marketplace add https://github.com/shinyorg/skills
copilot plugin install controls@shiny
View controls Plugin