Skip to content

Toast

A toast is a short, time-limited notification that appears in a corner of the screen. The installToast behavior listens for hc:toast events on document.body, renders the toast into the page’s toast region, and auto-dismisses it after a few seconds.

Each button POSTs to a demo endpoint that answers 204 No Content plus an HX-Trigger: {"hc:toast":…} header — htmx dispatches the event on <body> and installToast renders it. Unlike the client-dispatched preview further down the page, these toasts round-trip through a real endpoint: a namespaced demo implementation of the server contract under api/recipes/toast/. The Sticky error toast has duration: 0, so it stays until the page reloads.

The toast region is a single host element. Place it once near the end of <body>. If the region is missing, the behavior creates one automatically.

<div
class="hc-toast-region"
data-hc-toast-region
role="region"
aria-label="Notifications">
</div>

Dispatch a bubbling hc:toast event with a detail object:

document.body.dispatchEvent(
new CustomEvent('hc:toast', {
bubbles: true,
detail: { message: 'Saved', variant: 'success' },
})
);

Try it — each button dispatches the event above. The toast appears in the page corner (and auto-dismisses):

Return an HX-Trigger response header. htmx dispatches hc:toast on the body for you.

HTTP/1.1 200 OK
HX-Trigger: {"hc:toast":{"message":"Saved","variant":"success"}}

For multiple events in one response:

HX-Trigger: {"hc:toast":{"message":"Saved","variant":"success"}, "items:refresh":true}
FieldTypeDefaultNotes
messagestring(required)The body text.
titlestring(omitted)Bold one-liner above the message.
variantstring'info'info / success / warning / error.
durationnumber4500Milliseconds. 0 keeps the toast until removed manually.
idstring(omitted)Stable handle: a later hc:toast with the same id updates the existing toast in place instead of stacking a new one.
actionobject(omitted){ label, event } — renders an action button; activating it dispatches a bubbling CustomEvent(event) with detail: { id, action, toast }, then dismisses the toast.

variant="error" also flips the toast to role="alert" / aria-live="assertive" so screen readers interrupt to announce it. Other variants use role="status" / aria-live="polite".

id and action combine into the two server-driven patterns other recipes build on:

  • Update by id (loading → success / error): send a first toast with { id: 'job-42', message: 'Exporting…', duration: 0 }, then a second hc:toast with the same id — the visible toast morphs in place (used by the promise-toast pattern in Toast → Update by id).
  • Action (e.g. Undo): { action: { label: 'Undo', event: 'hc:undo' } } renders a button; htmx can listen with data-hx-trigger="hc:undo from:body" — see the undo-delete recipe. Every toast also renders a close button (.hc-toast__close), so a sticky toast (duration: 0) stays dismissable with a pointer even without an action; Escape dismisses the toast that contains the focus (without firing the action’s event).
// Success
document.body.dispatchEvent(new CustomEvent('hc:toast', {
bubbles: true,
detail: { message: 'Invoice sent.' },
}));
// Error, sticky until dismissed
document.body.dispatchEvent(new CustomEvent('hc:toast', {
bubbles: true,
detail: {
title: 'Sync failed',
message: 'Could not reach the server. Retrying…',
variant: 'error',
duration: 0,
},
}));

From a server-side handler:

# Django
from django.http import HttpResponse
import json
def delete_item(request, id):
item = get_object_or_404(Item, pk=id)
item.delete()
return HttpResponse('', headers={
'HX-Trigger': json.dumps({
'hc:toast': {'message': 'Deleted.', 'variant': 'success'},
}),
})
  • info / success / warning use role="status" (polite) so they do not interrupt the user’s flow.
  • error uses role="alert" (assertive) so failures are announced immediately. Reserve this for genuine errors.
  • The region itself is a labeled region landmark — assistive tech can navigate to it.
  • Toasts disappear automatically; do not put critical information here that the user must act on. Confirmations of completed actions are the canonical use case.

Without hc.behaviors.js, the hc:toast event is dispatched but no listener exists; nothing renders. The HTTP response itself is still valid — HX-Trigger is a no-op for plain fetch() calls.

For applications that must communicate confirmation without JavaScript, server-render a flash message inside the page (an hc-alert is the right component) and remove it from the next response.

  • Alert — block-level inline notice; permanent until the page reloads.
  • Confirm action — often paired with a toast on success.