Toast
A short, non-interrupting notification about the result of an action, shown from the server or from JavaScript.
<sa-button variant="ButtonVariant.Outline" id="--toast-intro-button">Save itinerary</sa-button>
<!-- Add the toaster once, in your layout -->
<sa-toaster />
<script type="module">
document.getElementById("--toast-intro-button").addEventListener("click", () =>
window.stellarAdmin.toast.success("Itinerary saved", "Your Lisbon trip is ready to share."));
</script>Usage
Add <sa-toaster> once to your layout. Place it outside any region that htmx or another AJAX library swaps, so the toaster and its open toasts survive the swap.
<!-- _Layout.cshtml -->
<body>
@RenderBody()
<sa-toaster />
</body>AddTagHelpers() registers everything else, so there is no separate setup call and no middleware.
Show a toast from a controller action or Razor Pages handler by injecting IToastNotifier:
public class BookingsController(IToastNotifier toasts) : Controller
{
[HttpPost]
public IActionResult Confirm(int id)
{
// ...
toasts.Success("Booking confirmed", "Your trip to Lisbon departs 14 October.");
return RedirectToAction(nameof(Details), new { id });
}
}Or show one from JavaScript with window.stellarAdmin.toast:
window.stellarAdmin.toast.success("Copied to clipboard");Toasts close on their own after 5 seconds. Hovering over or focusing the toaster pauses the timers, as does switching to another tab or window. Every toast has a close button, can be swiped away, and closes with Esc when focused. F6 moves focus to the toaster, so keyboard users can reach a toast before it closes. Toasts are announced politely to screen readers, and error toasts are also announced assertively.
Examples
Types
Each type shows its own icon. Loading toasts show a spinner and stay open until they are updated or closed, unless you give them a duration.
<div class="flex flex-wrap justify-center gap-2">
<sa-button variant="ButtonVariant.Outline" data-toast-type="default">Default</sa-button>
<sa-button variant="ButtonVariant.Outline" data-toast-type="success">Success</sa-button>
<sa-button variant="ButtonVariant.Outline" data-toast-type="info">Info</sa-button>
<sa-button variant="ButtonVariant.Outline" data-toast-type="warning">Warning</sa-button>
<sa-button variant="ButtonVariant.Outline" data-toast-type="error">Error</sa-button>
<sa-button variant="ButtonVariant.Outline" data-toast-type="loading">Loading</sa-button>
</div>
<sa-toaster />
<script type="module">
const toast = window.stellarAdmin.toast;
const types = {
default: () => toast.add({ title: "Itinerary archived", description: "3 bookings moved to archive." }),
success: () => toast.success("Booking confirmed", "Your trip to Lisbon departs 14 October."),
info: () => toast.info("Fare alert", "Flights to Kyoto dropped 12% this week."),
warning: () => toast.warning("Passport expires soon", "Renew it before your March trip to Cape Town."),
error: () => toast.error("Payment declined", "Your card ending 4242 was declined."),
loading: () => toast.add({ title: "Checking seat availability...", type: "loading", duration: 3000 }),
};
document.querySelectorAll("[data-toast-type]").forEach((button) =>
button.addEventListener("click", () => types[button.dataset.toastType]()));
</script>Action
Add a link to the toast with action. Actions are links rather than buttons, so they work the same whether the toast came from the server or from JavaScript. Use them to take the reader somewhere, such as the record they just changed.
<sa-button variant="ButtonVariant.Outline" id="--toast-action-button">Book hotel</sa-button>
<sa-toaster />
<script type="module">
document.getElementById("--toast-action-button").addEventListener("click", () =>
window.stellarAdmin.toast.success("Hotel booked", {
description: "Casa do Mar, Lisbon. 3 nights from 14 October.",
action: { label: "View", href: "#booking-2048" },
}));
</script>From the server, create the action with ToastAction.Link:
toasts.Add(new Toast
{
Title = "Hotel booked",
Description = "Casa do Mar, Lisbon. 3 nights from 14 October.",
Type = ToastType.Success,
Action = ToastAction.Link("View", Url.Action("Details", new { id = booking.Id })!),
});Promise
promise() shows a loading toast until a promise settles, then turns it into a success or error toast. The messages can be strings or functions that receive the result or the error. It returns the original promise, so you can still await it.
<sa-button variant="ButtonVariant.Outline" id="--toast-promise-button">Request refund</sa-button>
<sa-toaster />
<script type="module">
function requestRefund() {
return new Promise((resolve) => setTimeout(() => resolve({ amount: "€284.00" }), 2000));
}
document.getElementById("--toast-promise-button").addEventListener("click", () =>
window.stellarAdmin.toast.promise(requestRefund(), {
loading: "Requesting your refund...",
success: (refund) => `Refund of ${refund.amount} requested`,
error: "We couldn't request your refund",
}));
</script>To update a toast yourself, keep the id that add() returns:
const id = window.stellarAdmin.toast.add({ title: "Uploading documents...", type: "loading" });
// Later
window.stellarAdmin.toast.update(id, { title: "Documents uploaded", type: "success", duration: 5000 });Position
Set where toasts appear with the position attribute. The default is ToasterPosition.BottomRight. Toasts can be swiped towards the nearest edges of their position.
<sa-toaster position="ToasterPosition.TopCenter" />Duration and limit
duration sets how long toasts stay open when they don't set their own, and limit sets how many are visible at once. Older toasts are hidden until newer ones close.
<sa-toaster duration="TimeSpan.FromSeconds(8)" limit="5" />A toast can set its own duration, in milliseconds from JavaScript or as a TimeSpan on the server. A duration of zero keeps the toast open until it is closed.
window.stellarAdmin.toast.error("Payment declined", { description: "Your card ending 4242 was declined.", duration: 0 });Toasts from the server
IToastNotifier queues toasts in TempData. The library then decides how each response delivers them:
| Response | Where the toast appears |
|---|---|
| A full page load | On that page, rendered by <sa-toaster>. |
| A redirect | On the page the browser is redirected to. |
An AJAX request (fetch, XMLHttpRequest, htmx) | In the SA-Toasts response header, which your script passes to fromResponse(). |
The browser marks AJAX requests with the Sec-Fetch-Mode header, so no configuration is needed. If a request has no Sec-Fetch-Mode header, it is treated as a page load and the toast appears on the next page instead.
Add toasts in actions and handlers, not in views. Toasts added while a view renders miss the current response's header and appear on the next response instead.
The header holds up to about 4 KB of toasts. Any that don't fit stay queued for the next response.
Showing toasts after AJAX requests
The library doesn't intercept AJAX requests. Instead, pass the response to window.stellarAdmin.toast.fromResponse(), which shows the toasts in the SA-Toasts header. It accepts a Response, its Headers, an XMLHttpRequest, or the header's value, and does nothing when the header is missing.
For htmx 4, add a listener once in your layout:
document.addEventListener("htmx:after:request", (e) =>
window.stellarAdmin.toast.fromResponse(e.detail.ctx.response.headers));For htmx 2:
document.addEventListener("htmx:afterRequest", (e) =>
window.stellarAdmin.toast.fromResponse(e.detail.xhr));For fetch, call it after each request, usually in your app's own fetch helper:
const response = await fetch(url, options);
window.stellarAdmin.toast.fromResponse(response);fetch follows redirects, so a toast added before a redirect arrives in the header of the redirected response.
Limitations
- htmx's
HX-RedirectandHX-Locationheaders are sent with a 200 response, so the toast goes into the header and is lost when the page navigates away. Use a normal redirect when a toast must survive navigation. - With
hx-boostor any other swap of the whole body, keep<sa-toaster>outside the swapped region. - Only link actions can be sent from the server.
API Reference
<sa-toaster>
Renders the region that shows toasts. Classes set on it apply to the viewport that positions the toasts.
Prop
Type
IToastNotifier
Inject it into controllers, page models and services. Add(Toast) queues a toast. The Success, Info, Warning and Error extension methods take a title and an optional description.
Toast
Prop
Type
window.stellarAdmin.toast
Every method throws if the page has no <sa-toaster>.
Prop
Type
ToastOptions
Prop
Type