Files
Htmx/docs/Components/ToastViewport.md
T
2026-05-05 23:55:26 +05:00

3.9 KiB

ToastViewport

The fixed container where toast notifications appear. Place exactly one ToastViewport in your main layout — it sits in the corner of the screen and is invisible when no toasts are showing. New toasts stack upward as they are added.


Quick example

<!-- MainLayout.htmx -->
<body>
  <main>$$Body$$</main>
  $$ToastViewport$$
</body>
// MainLayout.htmx.cs
_toastViewport = new ToastViewport();

That's all. Every call to window.showToast(...) will now display in the bottom-right corner of the screen.


All the options

public ToastViewport(string id = "toast-viewport")
Parameter What it does
id The element id. components.js looks for #toast-viewport by default. Only change this if you also update the JavaScript.

How it works

ToastViewport renders a single fixed <div> anchored to the bottom-right of the screen. It has pointer-events: none so it doesn't block clicks on the page behind it. Individual toasts set pointer-events: auto so their dismiss buttons are still clickable.

Toasts are appended to this element by window.showToast() and removed after their duration expires. }


---

## Tips and tricks

- Place `ToastViewport` once in the outermost layout — not inside HTMX swap targets, since HTMX replaces the target's contents and would remove the viewport.
- The default id `"toast-viewport"` is hard-coded in `components.js` for the `showToast` lookup. If you rename it, update the JS too.
- `ToastViewport` renders as an empty `div` — it has no visual presence until a toast is appended to it.
- Multiple viewports on the same page are valid for different toast regions (e.g. top-right and bottom-right), but `showToast` will target whichever viewport id it is configured for.
- Multiple viewports on the same page are valid for different toast regions (e.g. top-right and bottom-right), but `showToast` will target whichever viewport id it is configured for.

---

## Complete page example

`ToastViewport` is a layout-level concern — it lives in `MainLayout`, not in individual page templates.  The example below shows the full integration pattern.

**`Templates/MainLayout.htmx`** (excerpt)
```html
<body class="min-h-screen bg-background text-foreground">
  $$NavBar$$
  <main class="container mx-auto px-4 py-8">
    $$Content$$
  </main>
  $$ToastViewport$$
  $$Scripts$$
</body>

Templates/MainLayout.htmx.cs (excerpt)

namespace Htmx.ApiDemo.Templates;

public sealed class MainLayout : MainLayoutBase
{
    private readonly IHtmxComponent _nav;
    private readonly IHtmxComponent _content;
    private readonly IHtmxComponent _viewport;

    public MainLayout(IHtmxComponent content, IHtmxComponent nav)
    {
        _nav      = nav;
        _content  = content;

        // Place one viewport at bottom-right for all pages
        _viewport = new Components.ToastViewport(
            id:       "toast-viewport",
            position: "bottom-right");
    }

    protected override void RenderNavBar(HtmxRenderContext ctx)       => _nav.Render(ctx.Next());
    protected override void RenderContent(HtmxRenderContext ctx)      => _content.Render(ctx.Next());
    protected override void RenderToastViewport(HtmxRenderContext ctx) => _viewport.Render(ctx.Next());
}

Triggering a toast from any handler

// Any POST handler can fire a toast without changing the ToastViewport markup.
ctx.Response.Headers["HX-Trigger"] =
    """{"showToast":{"title":"Saved!","description":"Your changes have been persisted."}}""";

Two-viewport layout (top-right errors + bottom-right successes)

_errorViewport   = new Components.ToastViewport(id: "error-viewport",   position: "top-right");
_successViewport = new Components.ToastViewport(id: "success-viewport", position: "bottom-right");

// Error toast
ctx.Response.Headers["HX-Trigger"] =
    """{"showToast":{"viewportId":"error-viewport","title":"Something went wrong."}}""";