Toast notifications for ASP.NET Core MVC and Razor Pages - straight from your C# code.
_notyf.Success("Order placed!");That's it. The toast shows up on the page, whether you redirect, return a view, or call the endpoint with fetch, jQuery or htmx.
- ✅ .NET 8 and .NET 10
- ✅ MVC, Razor Pages and Minimal APIs
- ✅ Works after redirects (TempData), form posts,
fetch, jQuery AJAX and htmx - ✅ No jQuery required
- ✅ No inline scripts - works with a strict Content Security Policy
- ✅ Two JS libraries to pick from: Notyf and Toastify
- ✅ Custom colours, icons, CSS classes, positions, RTL and sticky toasts
dotnet add package AspNetCoreHero.ToastNotificationbuilder.Services.AddNotyf();
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseNotyf(); // shows toasts raised during fetch / AJAX / htmx callsYou don't need any extra using lines for these two.
Open Views/Shared/_Layout.cshtml (or Pages/Shared/_Layout.cshtml) and add this just before </body>:
@await Component.InvokeAsync("Notyf")Inject INotyfService and call it:
public class OrdersController(INotyfService notyf) : Controller
{
[HttpPost]
public IActionResult Create(CreateOrderRequest request)
{
// save the order...
notyf.Success("Order placed!");
return RedirectToAction(nameof(Index));
}
}Run the app and you'll see the toast after the redirect. That's the whole setup.
notyf.Success("Order placed!");
notyf.Error("Payment failed.");
notyf.Warning("Stock is running low.");
notyf.Information("Shipping starts Monday.");
notyf.Custom("Deployed to production", 5, "#5b30d6", "fa fa-rocket");notyf.Success("Gone in 2 seconds", 2); // duration in seconds
notyf.Warning("Uses the global default"); // null = the DurationInSeconds you configured
notyf.Error("Stays until you close it", 0); // 0 = stickySticky toasts always get a close button.
This works out of the box once app.UseNotyf() is in place. Raise the toast in your endpoint as usual:
[HttpPost]
public IActionResult Archive(int id)
{
notyf.Success("Order archived.");
return NoContent();
}Then call it however you like - the toast shows up on its own:
await fetch("/orders/archive/42", { method: "POST" });The same goes for jQuery ($.post(...)), plain XMLHttpRequest and htmx. Error responses (400, 500...) carry toasts too.
How it works: for same-origin requests the library adds an X-Requested-With header, the server sends the toasts back in a response header, and the script shows them. For cross-origin calls, add X-Requested-With: XMLHttpRequest to the request yourself.
Minimal APIs work the same way:
app.MapPost("/api/orders/{id:int}/cancel", (int id, INotyfService notyf) =>
{
notyf.Warning($"Order #{id} cancelled.");
return Results.NoContent();
});Every setting is optional.
builder.Services.AddNotyf(config =>
{
config.DurationInSeconds = 5;
config.Position = NotyfPosition.BottomRight;
config.IsDismissable = true;
// Per-type colours, classes and icons
config.Success.BackgroundColor = "#0f766e";
config.Error.ClassName = "shake";
config.Warning.IconClassName = "bi bi-exclamation-triangle";
// Everything else
config.IsRtl = true;
config.ClassName = "brand-toast";
config.IncludeFontAwesome = false;
});| Setting | Default | What it does |
|---|---|---|
DurationInSeconds |
5 |
Default duration for every toast. |
Position |
BottomRight |
TopRight, BottomRight, BottomLeft, TopLeft, TopCenter, BottomCenter, TopFullWidth, BottomFullWidth. |
IsDismissable |
false |
Shows a close button on every toast. |
HasRippleEffect |
true |
Notyf's ripple animation. |
IsRtl |
false |
Right-to-left layout. |
ClassName |
- | Extra CSS class on every toast. |
Success, Error, Warning, Information, Custom |
v1 colours | BackgroundColor, ClassName and IconClassName for each type. |
IncludeFontAwesome |
true |
Loads Font Awesome 4.7 for the Warning / Information icons. Turn it off if you already load your own icons. |
FontAwesomeUrl |
CDN | Point it to a local copy if you don't want the CDN. |
AutoHandleAjax |
true |
Shows toasts from fetch / AJAX / htmx responses automatically. |
You can also pass a CSS class to a single toast:
notyf.Custom("Welcome back, <b>Mukesh</b>!", 5, "#1b1712", "fa fa-hand-peace-o", className: "is-greeting");Prefer Toastify's look? Swap the three lines:
builder.Services.AddToastify(config =>
{
config.DurationInSeconds = 5;
config.Gravity = Gravity.Bottom; // Top or Bottom
config.Position = Position.Right; // Left or Right
});
app.UseToastify();@await Component.InvokeAsync("Toastify")Then inject IToastifyService instead of INotyfService. The methods are the same. Custom takes any CSS background, gradients included:
toastify.Custom("Deployed!", 5, "linear-gradient(135deg, #5b30d6, #d63085)");Messages are rendered as HTML. That's what lets you use <b> or <br> in a toast. It also means you should never pass raw user input. Encode it first:
notyf.Error($"Could not save {HtmlEncoder.Default.Encode(request.Name)}");Content Security Policy. v2 doesn't render any inline JavaScript. The toasts travel in a JSON data block, so script-src 'self' works as is. If your CSP uses nonces, pass yours in:
@await Component.InvokeAsync("Notyf", new { nonce = Context.Items["csp-nonce"] })The samples/ToastNotification.Docs app is the documentation site, and it runs locally:
cd samples/ToastNotification.Docs
dotnet runWhat you get:
- A live playground - build a toast, watch the C# code update, and fire it from the server.
- Notyf and Toastify side by side, so you can pick the one you like.
- A setup wizard that gives you the exact code for MVC, Razor Pages or Minimal APIs.
- Every docs section with a button to try it, and a toggle to switch the code between Notyf and Toastify.
It uses all three workloads for real: MVC controllers, a Razor Page (/checkout) and Minimal API endpoints. It registers both libraries only so it can compare them - your app needs just one.
| Symptom | Cause | Fix |
|---|---|---|
404 on /_content/AspNetCoreHero.ToastNotification/... |
You're running an unpublished app outside the Development environment, so static web assets are off. | Publish the app, or add builder.WebHost.UseStaticWebAssets();. |
| No toast after a fetch / AJAX call | app.UseNotyf() is missing, or the call goes to another origin. |
Add UseNotyf(). For cross-origin calls, send X-Requested-With: XMLHttpRequest. |
| Toast shows twice after upgrading from v1 | Your code still calls getResponseHeaders(xhr). |
Remove the call. v2 handles AJAX on its own. |
| Warning / Information icons are missing | IncludeFontAwesome = false and no other icon font on the page. |
Load Font Awesome yourself, or set IconClassName for those types. |
Most apps only need to bump the package version. Here's what changed:
- Targets .NET 8 and .NET 10. .NET Core 3.1 and .NET 5 are no longer supported. Stay on 1.1.0 if you need them.
- jQuery is no longer needed, and the component can sit anywhere before
</body>. - Duration
0now means sticky. In v1 it meant "use the default". Passnull(or nothing) for the default. - AJAX works automatically, including
fetchand htmx. Remove any manualgetResponseHeaders(xhr)calls. - Newtonsoft.Json is gone. The library uses System.Text.Json now, and the internal
JsonSerializationhelper is no longer public. AddNotyf()/AddToastify()now returnIServiceCollectionand live in theMicrosoft.Extensions.DependencyInjectionnamespace.UseNotyf()/UseToastify()live inMicrosoft.AspNetCore.Builder. Your existing code still compiles.INotyfService.CustomandIToastifyService.Customhave a new optionalclassNameparameter. If you wrote your own implementation of these interfaces, add the parameter.- Toastify now supports AJAX too. Add
app.UseToastify().
The full list is in the CHANGELOG.
Issues and pull requests are welcome. To build and test locally:
dotnet build
dotnet test --solution AspNetCoreHero.ToastNotification.slnxThe browser tests use Playwright. Install Chromium once with:
pwsh tests/AspNetCoreHero.ToastNotification.E2ETests/bin/Debug/net10.0/playwright.ps1 install chromiumThis package wraps two great open-source libraries: Notyf by Carlos Roso and Toastify by Varun A P.
If this package saves you time, consider supporting it.
I'm Mukesh Murugan. I write about .NET at codewithmukesh.com - free courses, deep dives and a weekly newsletter for .NET developers.
- LinkedIn - Mukesh Murugan
- X - @iammukeshm
MIT
