M
Marai.UI
v1.0.0-alpha.7

Toast

Transient, non-blocking notification messages that appear temporarily and auto-dismiss. Display confirmations, errors, warnings, or status updates without interrupting the user's workflow. Style toasts freely with Tailwind CSS classes.

Overview

The MToast component provides a clean, accessible way to display temporary notifications. It features nine flexible placement positions, configurable duration, and automatic FIFO (First-In-First-Out) dismissal. The toast system is backed by MToastService, allowing you to trigger notifications from anywhere in your application. Visual appearance is entirely up to you — pass any Tailwind CSS classes via the class parameter on Show().

Key Features:

  • Nine placement options (all corners, centers, and edges)
  • Fully class-driven styling — pass any Tailwind CSS classes
  • Auto-dismissal with configurable duration
  • FIFO queue management (removes oldest toast first)
  • Portals to document.body — always renders above all content

Usage

Add the namespace imports and register the service in your Program.cs:

csharp
// Program.cs
builder.Services.AddMaraiUI();
razor
@using Marai.UI.Components.MToast
@inject MToastService ToastService

Add the Toaster in App.razor:

razor
<MToaster />

Basic Usage

Place <MToaster /> once in your root layout (e.g., MainLayout.razor or App.razor). Then inject MToastService anywhere you need to show notifications:

Preview
razor
@inject MToastService ToastService

<MButton OnClick="ShowToast">Show Toast</MButton>

@code {
    private void ShowToast()
        => ToastService.Show("Your changes have been saved successfully.");
}

Examples

Styling with Tailwind Classes

Pass any Tailwind CSS classes via the class parameter to control each toast's appearance. This gives you complete control over background color, text color, padding, width, border radius, and shadow.

Preview
razor
@inject MToastService ToastService

<div style="display:flex;gap:0.5rem;flex-wrap:wrap;">
    <MButton Class="inline-flex items-center justify-center rounded-md bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700" OnClick='@(() => ToastService.Show("Primary notification message", "w-80 p-4 bg-blue-600 text-white rounded-lg shadow-lg"))'>Blue</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-500 px-4 py-2 text-sm font-medium text-white hover:bg-slate-600" OnClick='@(() => ToastService.Show("Secondary information", "w-80 p-4 bg-slate-500 text-white rounded-lg shadow-lg"))'>Slate</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-emerald-600 px-4 py-2 text-sm font-medium text-white hover:bg-emerald-700" OnClick='@(() => ToastService.Show("Operation completed successfully!", "w-80 p-4 bg-emerald-600 text-white rounded-lg shadow-lg"))'>Emerald</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-red-600 px-4 py-2 text-sm font-medium text-white hover:bg-red-700" OnClick='@(() => ToastService.Show("Error: Something went wrong", "w-80 p-4 bg-red-600 text-white rounded-lg shadow-lg"))'>Red</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-amber-500 px-4 py-2 text-sm font-medium text-white hover:bg-amber-600" OnClick='@(() => ToastService.Show("Warning: Action requires attention", "w-80 p-4 bg-amber-500 text-white rounded-lg shadow-lg"))'>Amber</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-sky-500 px-4 py-2 text-sm font-medium text-white hover:bg-sky-600" OnClick='@(() => ToastService.Show("Information: Background sync in progress", "w-80 p-4 bg-sky-500 text-white rounded-lg shadow-lg"))'>Sky</MButton>
</div>

Placement Options

Control where each toast appears by passing a placement parameter to Show(). Nine positions are available. Each call can target a different position independently.

Preview
razor
@inject MToastService ToastService

<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:0.5rem;max-width:600px;">
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick="@(() => ShowToastAt(ToastPlacement.TopLeft))">Top Left</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick="@(() => ShowToastAt(ToastPlacement.TopCenter))">Top Center</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick="@(() => ShowToastAt(ToastPlacement.TopRight))">Top Right</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick="@(() => ShowToastAt(ToastPlacement.MiddleLeft))">Middle Left</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick="@(() => ShowToastAt(ToastPlacement.MiddleCenter))">Middle Center</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick="@(() => ShowToastAt(ToastPlacement.MiddleRight))">Middle Right</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick="@(() => ShowToastAt(ToastPlacement.BottomLeft))">Bottom Left</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick="@(() => ShowToastAt(ToastPlacement.BottomCenter))">Bottom Center</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick="@(() => ShowToastAt(ToastPlacement.BottomRight))">Bottom Right</MButton>
</div>

@code {
    private void ShowToastAt(ToastPlacement placement)
        => ToastService.Show($"Toast at {placement}", "w-80 p-4 bg-slate-800 text-white rounded-lg shadow-lg", 3000, placement);
}

Dark and Light Themes

Because toasts are styled entirely with Tailwind classes, any visual theme is achievable. Use bg-slate-900 text-slate-50 for dark toasts, bg-white text-slate-800 border for light ones. Pass a Class to <MToaster /> as a default fallback for toasts that don't specify their own class.

Preview
razor
@inject MToastService ToastService

<div style="display:flex;gap:0.5rem;flex-wrap:wrap;">
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800" OnClick="ShowDark">Dark Toast</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50" OnClick="ShowLight">Light Toast</MButton>
</div>

@code {
    private void ShowDark()
        => ToastService.Show("Dark themed notification", "w-80 p-4 bg-slate-900 text-slate-50 border border-slate-700 rounded-lg shadow-xl", 3000);

    private void ShowLight()
        => ToastService.Show("Light themed notification", "w-80 p-4 bg-white text-slate-800 border border-slate-200 rounded-lg shadow-lg", 3000);
}

Custom Duration

Set a custom duration in milliseconds. Pass 0 to create a persistent toast that remains until manually dismissed.

Preview
razor
@inject MToastService ToastService

<div style="display:flex;gap:0.5rem;flex-wrap:wrap;">
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800" OnClick='@(() => ToastService.Show("Quick message (2s)", "w-80 p-4 bg-slate-800 text-white rounded-lg shadow-lg", 2000))'>2 Seconds</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800" OnClick='@(() => ToastService.Show("Standard message (5s)", "w-80 p-4 bg-slate-800 text-white rounded-lg shadow-lg", 5000))'>5 Seconds (Default)</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800" OnClick='@(() => ToastService.Show("Longer message (10s)", "w-80 p-4 bg-slate-800 text-white rounded-lg shadow-lg", 10000))'>10 Seconds</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50" OnClick='@(() => ToastService.Show("This stays until dismissed", "w-80 p-4 bg-amber-500 text-white rounded-lg shadow-lg", 0))'>Persistent (0)</MButton>
</div>

Sizing

Control toast width and padding using Tailwind utilities in the class parameter passed to Show().

Preview
razor
@inject MToastService ToastService

<div style="display:flex;gap:0.5rem;flex-wrap:wrap;align-items:center;">
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-3 py-1.5 text-sm font-medium text-white hover:bg-slate-800" OnClick='@(() => ToastService.Show("Compact toast", "w-60 p-2 text-xs bg-slate-800 text-white rounded shadow-md", 3000))'>Compact</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800" OnClick='@(() => ToastService.Show("Default toast notification", "w-80 p-4 bg-slate-800 text-white rounded-lg shadow-lg", 3000))'>Default</MButton>
    <MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800" OnClick='@(() => ToastService.Show("Wide toast notification with more space", "w-96 p-4 bg-slate-800 text-white rounded-xl shadow-xl", 3000))'>Wide</MButton>
</div>

Multiple Toasts

Each call to Show() adds a new toast to the stack. They auto-dismiss independently using FIFO order (First-In-First-Out — oldest toast is removed first when duration expires).

Preview
razor
@inject MToastService ToastService

<MButton Class="inline-flex items-center justify-center rounded-md bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800" OnClick="ShowMultiple">Show Multiple Toasts</MButton>

@code {
    private void ShowMultiple()
    {
        ToastService.Show("First notification", "w-80 p-4 bg-sky-600 text-white rounded-lg shadow-lg", 4000);
        Task.Delay(500).ContinueWith(_ => ToastService.Show("Second notification", "w-80 p-4 bg-emerald-600 text-white rounded-lg shadow-lg", 4000));
        Task.Delay(1000).ContinueWith(_ => ToastService.Show("Third notification", "w-80 p-4 bg-amber-500 text-white rounded-lg shadow-lg", 4000));
    }
}

Accessibility

  • Normal toasts render with role="status" — screen readers announce the message politely without interrupting the user
  • Alert toasts (errors, destructive actions) render with role="alert" — screen readers announce the message assertively and immediately
  • The dismiss button includes aria-label="Dismiss notification" and is always keyboard reachable
  • The dismiss icon carries aria-hidden="true" — the accessible label comes from the button itself
  • Focus is never moved automatically to a toast — toasts are non-blocking and do not interrupt the current workflow

Use ShowAlert() for errors, payment failures, destructive confirmations, or any message that must be read immediately:

csharp
// Normal — announced politely
ToastService.Show("Report exported successfully.", "bg-slate-900 text-white ...");

// Alert — announced immediately
ToastService.ShowAlert("Payment failed. Please check your card details.", "bg-red-600 text-white ...");

API Reference

MToastService

Member Type Description
Toasts IReadOnlyList<MToastItem> Snapshot of all currently active toasts.
OnChanged Action? Fires whenever the toast list changes (add or remove).
Show(message, class, duration, placement) void Adds a new toast. class is an optional Tailwind CSS string applied to the toast element. duration defaults to 5000 ms; pass 0 to disable auto-dismiss. placement defaults to ToastPlacement.BottomRight.
Dismiss(id) void Removes the toast with the given Guid id.

MToaster

Parameter Type Default Description
Class string? null Default CSS classes applied to all toasts. Overridden when a per-toast class is passed to Show().

MToast

Parameter Type Default Description
Item MToastItem required The toast data record to render.
OnDismiss EventCallback Invoked when the close button is clicked.
Class string? null CSS classes applied to this toast element. Falls back to MToaster.Class when not set.
AdditionalAttributes Dictionary<string, object>? null Arbitrary HTML attributes passed through to the toast element.

MToastItem

Property Type Description
Id Guid Unique identifier assigned on creation.
Message string Text content of the notification.
Class string? Optional Tailwind CSS classes controlling this toast's visual appearance.
Duration int Auto-dismiss delay in milliseconds. 0 = persistent.
Placement ToastPlacement Screen position for this toast. Defaults to BottomRight.

ToastPlacement Enum

Value Position Best Used For
TopLeft Top-left corner System notifications, background updates
TopCenter Top-center of screen Important alerts that need immediate attention
TopRight Top-right corner Non-intrusive notifications
BottomLeft Bottom-left corner Status updates, progress notifications
BottomCenter Bottom-center of screen Cookie consents, temporary messages
BottomRight Bottom-right corner (default) Standard notifications, success messages
MiddleLeft Middle-left edge Side navigation notifications
MiddleCenter Center of screen Critical warnings, modal-like alerts
MiddleRight Middle-right edge Contextual notifications related to sidebar content
Setup

Register the service in Program.cs with builder.Services.AddMaraiUI(), then place <MToaster /> once in your root layout. The component subscribes to MToastService automatically and renders all active toasts.

FIFO Queue Behavior

When multiple toasts are active and a duration expires, the oldest toast is removed first (First-In-First-Out). This ensures that users see messages in the order they were triggered, preventing confusion when multiple notifications appear rapidly.

Body Portal

MToaster moves its container to document.body on first render, bypassing any CSS stacking context from ancestor elements. This guarantees toasts remain visible above modals, overlays, and other fixed-position content.