Error
Three placements, one voice. Inline for anything the user can fix in place: the shadcn/ui Field puts the message under its control in danger, and the edge and ring follow. Toast for a background failure with a retry. A modal only when work was lost or the next step is blocked, built on Alert dialog. Say what happened and what to do next; never blame, never a code without a sentence.
<Field data-invalid>
<FieldLabel htmlFor="email">Approver email</FieldLabel>
<Input id="email" aria-invalid aria-describedby="email-err" />
<FieldError id="email-err">Enter a full address, like finance@acme.com.</FieldError>
</Field>
toast.error("Couldn't reach the bank", { action: { label: "Retry", onClick: retry } });
<ErrorDialog open={failed} onOpenChange={setFailed} title="The payment didn't go through" description="…" onRetry={retry} />Inline
One message as a line; several as a list, duplicates collapsed. The label stays in ink.
Used for the dashboard only; agents never see it.
- At least 12 characters.
- At least one number.
Form summary
When more than one field fails on submit: a danger Alert at the top linking to each field, plus the inline messages.
Modal
Alert dialog on a danger tile. Retry is primary when the action can be repeated; a details block carries the reference.
Toast
Sonner's error recipe: danger-soft on a danger edge, five seconds, Retry as the action.