UI alerts (toasts)
View on GitHubUI alerts (toasts)
The small, transient toast in the top-right corner of the screen — a
one-line confirmation or error, auto-dismissing after a few seconds —
is a completely different system from
notificationsNotifications (in-app & email)Orbit tells users about activity two ways: an in-app notification (the bell dropdown) and an email, per App\Enums\Notifications\NotificationType — per-user…: it's client-side only,
ephemeral, never persisted, and has nothing to do with
NotificationType/NotificationSetting. This category documents how
it works and the two independent ways to trigger one.
Guides, in the order you'd actually need them
- Trigger an alert from a backend actionUI alerts (toasts)Trigger an alert from a backend actionWorked example: adding a "View in settings" link to the existing notification-settings confirmation toast. This is also the guide to read if you just want…
— the zero-frontend-code way: flash a specific key on a redirect
response and it shows up as a toast automatically. Covers the one
easy mistake: the flash key vocabulary here is not the same as
the
Notificationmodel's severity vocabulary. - Trigger an alert from the frontendUI alerts (toasts)Trigger an alert from the frontendWorked example: IssuePageHeader's "copy issue link" button calls navigator.clipboard.writeText(...) today and gives the user no feedback at all — no page r…
— calling
addAlert()directly for actions that never round-trip through a redirect (a clipboard copy, an optimistic toggle). Worked example: adding the confirmation toast thatIssuePageHeader's "copy issue link" button doesn't have today. - Add a new alert typeUI alerts (toasts)Add a new alert typeWorked example: adding a fifth AlertType, neutral — a muted, non-urgent toast (e.g. "Draft saved") that shouldn't compete visually with the four existing i… — worked
example adding a fifth
AlertType,neutral, for a muted toast that shouldn't compete visually with success/error/warning/info. - Customize alert behaviorUI alerts (toasts)Customize alert behaviorAlertProvider has no limit on how many alerts can stack up, and no deduplication — every addAlert() call adds one more item, full stop (see resources/js/co… — worked example capping the visible stack at 5 (and a dedup-by-message variant), and where any future stacking/priority rule belongs.
- Testing components that use alertsUI alerts (toasts)Testing components that use alertsTwo different test shapes exist in this codebase already, depending on whether you're testing AlertContext itself vs. a component that merely consumes it.…
— the two different test shapes this codebase uses:
renderHookagainstuseAlert()directly with Inertia/AlertContainermocked out (forAlertContextbehavior itself), vs. a realAlertProviderwrapped around the component under test (for anything that merely consumes it).
The architecture in one paragraph
AlertProvider (mounted once, near the root of app.tsx) owns the
whole system: an in-memory list of AlertItems, rendered by
AlertContainer/Alert as a fixed top-right stack with a
framer-motion enter/exit animation, each auto-removed after a
duration (default 4000ms; pass 0 to keep it until the user
dismisses it manually). There are exactly two ways an alert gets
added to that list. Automatically: AlertProvider watches every
Inertia page load (an effect for the very first, server-rendered one)
and every subsequent visit (router.on('success', ...), chosen over a
usePage() effect specifically because Inertia reuses the same
flash object reference across visits with identical content, which
would silently skip a second identical flash under a naive effect) and
turns four specific flash keys — success, error, warning,
information — plus an optional sibling action_url key into a call
to addAlert(). Manually: any component can call useAlert().addAlert(message, type, duration, actionUrl)
directly, with no backend round-trip involved at all — see guide 2 for
when that's the right call.
