Alert
A message box for info, success, warnings, and errors, with an icon, title, and optional actions.
stableAlert.tsx
Examples
Tones
New version available
v0.2 adds Dialog, Tooltip, and Accordion.
Changes saved
Storage almost full
You’ve used 92% of your plan’s storage.
Payment failed
Your card was declined. Update it to keep your workspace.
alert/tones.tsx
import { Alert } from "@streamuilib/react";
export default function AlertTones() {
return (
<div className="flex w-full max-w-md flex-col gap-4">
<Alert tone="info" title="New version available">
v0.2 adds Dialog, Tooltip, and Accordion.
</Alert>
<Alert tone="success" title="Changes saved" />
<Alert tone="warning" title="Storage almost full">
You’ve used 92% of your plan’s storage.
</Alert>
<Alert tone="danger" title="Payment failed">
Your card was declined. Update it to keep your workspace.
</Alert>
</div>
);
}With actions
Deploy failed
The build step exited with code 1 after 42 seconds.
alert/with-action.tsx
import { Alert, Button } from "@streamuilib/react";
export default function AlertWithAction() {
return (
<Alert
tone="danger"
title="Deploy failed"
className="w-full max-w-md"
action={
<>
<Button size="sm">Retry deploy</Button>
<Button size="sm" variant="secondary">
View logs
</Button>
</>
}
>
The build step exited with code 1 after 42 seconds.
</Alert>
);
}Installation
bun add @streamuilib/reactusage.tsx
import { Alert } from "@streamuilib/react";
<Alert tone="success" title="Changes saved" />API
| Prop | Type | Default | Description |
|---|---|---|---|
| tone | "info" | "success" | "warning" | "danger" | "info" | Color, icon, and default role. |
| title | ReactNode | — | Bold first line. |
| icon | ReactNode | null | — | Replaces the tone's icon; null hides it. |
| action | ReactNode | — | Buttons or links under the message. |
| role | string | — | Overrides the default "alert" or "status". |
| ...props | ComponentProps<"div"> | — | Native div attributes. |
Accessibility
- Warnings and errors default to role="alert", which screen readers announce immediately; info and success use the politer role="status".
- Live roles announce content that appears after load. For a message that's on the page from the start, pass role="note" (or another role) so it isn't announced as new.
- The icon is decorative; the title and text carry the meaning, so color is never the only signal.
Source
packages/react/src/Alert.tsx
import type { ComponentProps, ReactNode } from "react";
import { CircleAlert, CircleCheck, Info, TriangleAlert } from "lucide-react";
import { cn } from "./cn";
export type AlertTone = "info" | "success" | "warning" | "danger";
const tones: Record<AlertTone, { surface: string; tile: string; icon: typeof Info }> = {
info: { surface: "bg-blue-soft", tile: "bg-blue", icon: Info },
success: { surface: "bg-lime-soft", tile: "bg-lime", icon: CircleCheck },
warning: { surface: "bg-orange-soft", tile: "bg-orange", icon: TriangleAlert },
danger: { surface: "bg-pink-soft", tile: "bg-pink", icon: CircleAlert },
};
export type AlertProps = Omit<ComponentProps<"div">, "title"> & {
tone?: AlertTone;
title?: ReactNode;
/** Replaces the tone's default icon. Pass `null` for no icon. */
icon?: ReactNode;
/** Buttons or links shown under the message. */
action?: ReactNode;
};
/**
* A message box. Warnings and errors use role="alert" so screen readers
* announce them right away; info and success use the politer role="status".
*/
export function Alert({ tone = "info", title, icon, action, className, children, role, ...props }: AlertProps) {
const t = tones[tone];
const urgent = tone === "warning" || tone === "danger";
const glyph = icon === undefined ? <t.icon className="size-5" strokeWidth={2.25} /> : icon;
return (
<div
role={role ?? (urgent ? "alert" : "status")}
className={cn("flex gap-4 border-2 border-ink p-4 text-ink shadow-brutal-sm", t.surface, className)}
{...props}
>
{glyph && (
<span
aria-hidden
className={cn("grid size-9 shrink-0 place-items-center border-2 border-ink text-on-bright", t.tile)}
>
{glyph}
</span>
)}
<div className="flex min-w-0 flex-1 flex-col gap-1 pt-0.5">
{title && <p className="font-bold">{title}</p>}
{children && <div className="text-sm leading-relaxed">{children}</div>}
{action && <div className="mt-2 flex flex-wrap gap-3">{action}</div>}
</div>
</div>
);
}