# ToastViewport The fixed container that holds all `Toast` notifications. Place exactly one `ToastViewport` in your main layout (e.g. `MainLayout.htmx`). The viewport is invisible when empty and stacks toasts upward as they are added. --- ## HTML structure ``` div[id={id}].toast-viewport.fixed.bottom-4.right-4.z-50.flex.flex-col-reverse.gap-2.w-80.pointer-events-none ``` --- ## CSS mechanics | Class | Effect | |---|---| | `fixed bottom-4 right-4` | Anchored to the bottom-right corner of the viewport | | `z-50` | Floats above all other content including dialogs and dropdowns | | `flex flex-col-reverse gap-2` | New toasts appear on top; older ones push downward | | `w-80` | Matches the default toast width | | `pointer-events-none` | The container itself doesn't capture clicks — toasts set `pointer-events-auto` individually | --- ## Constructor signature ```csharp public ToastViewport(string id = "toast-viewport") ``` | Parameter | Description | |---|---| | `id` | Element id (default: `"toast-viewport"`). `components.js` queries `#toast-viewport` by default — only change this if you also update the JS lookup. | --- ## Usage examples ### Place in MainLayout ```html
$$Body$$
$$ToastViewport$$ ``` ```csharp // MainLayout.htmx.cs public IHtmxComponent ToastViewport { get; } = new ToastViewport(); protected override void RenderToastViewport(HtmxRenderContext ctx) => ToastViewport.Render(ctx.Next()); ``` ### Custom id (advanced) ```csharp new ToastViewport(id: "notifications-container") ``` Then update the JS lookup: ```js // In components.js or a custom script: const viewport = document.getElementById('notifications-container'); ``` ### Custom position (bottom-left) The position is set by Tailwind classes on the rendered element. To change position, subclass the component or pass `extraClasses` if supported, or override the `toast-viewport` class in your `input.css`: ```css .toast-viewport { bottom: 1rem; left: 1rem; right: auto; } ``` --- ## 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 $$NavBar$$
$$Content$$
$$ToastViewport$$ $$Scripts$$ ``` **`Templates/MainLayout.htmx.cs`** (excerpt) ```csharp 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** ```csharp // 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) ```csharp _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."}}"""; ```