Feedback
Tell the user what happened: alerts, notifications, banners and empty or error states.
Alert & callout #
Inline messages in the page.
Alert #
Inline status message; danger/warning are assertive alerts.
<Alert tone="success" title="Deploy finished">
marketing-site is live on production.
</Alert>Slots: root body title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | content | Message text or content. | ||
title | string | Bold first line. | ||
tone | AlertTone | "info" | Colour and icon: info, success, warning or danger. |
AlertTone
type AlertTone = "info" | "success" | "warning" | "danger";Callout #
Highlighted note block with icon and tone (info, success, warning, danger).
<Callout tone="warning" title="Breaking change">
<code>size</code> no longer sets the heading level — use <code>order</code>.
</Callout>Slots: root body content icon title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | content | Note text or content. | ||
icon | content | Replace the tone icon (null hides it). | ||
title | string | Bold first line. | ||
tone | CalloutTone | "info" | Kind of note (sets colour and icon). |
CalloutTone
type CalloutTone = "info" | "tip" | "warning" | "danger" | "note";Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Notification & toast #
Messages that appear and go.
Notification #
Inline notification (distinct from toast host). Danger / warning announce assertively.
New sign-in
function Example() {
const visible = signal(true);
return (
<Show
when={visible()}
fallback={
<Button size="sm" variant="outline" onClick={() => visible.set(true)}>
Show notification
</Button>
}
>
<Notification tone="info" title="New sign-in" onClose={() => visible.set(false)}>
Chrome on macOS, Berlin — just now.
</Notification>
</Show>
);
}Slots: root body close content title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | content | Notification text. | ||
onClose | () => void | Shows a close button; called when it is pressed. | ||
title | string | Bold first line. | ||
tone | AlertTone | "info" | Colour and icon. |
AlertTone
type AlertTone = "info" | "success" | "warning" | "danger";ToastHost #
Live region for toasts. Existing toasts keep their DOM when others come and go; timers pause on hover/focus.
function Example() {
const toaster = createToaster();
return (
<>
<Group gap="0.5rem" wrap>
<Button onClick={() => toaster.push({ title: "Saved", message: "Your changes are live." })}>
Info toast
</Button>
<Button
variant="success"
onClick={() => toaster.push({ tone: "success", message: "Deployment finished." })}
>
Success
</Button>
<Button
variant="danger"
onClick={() =>
toaster.push({
tone: "danger",
message: "Build failed.",
action: { label: "Retry", onClick: () => {} },
})
}
>
Error with action
</Button>
</Group>
<ToastHost toaster={toaster} aria-label="Demo notifications" />
</>
);
}Slots: root action copy dismiss icon message title toast
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
toaster | Toaster | yes | The queue to render, from createToaster(). | |
position | ToastPosition | "bottom-right" | Screen corner or edge the toasts stack from. | |
render | (toast: ToastItem, dismiss: () => void) => unknown | Custom toast body; receives the item and a dismiss callback. |
Toaster
| Field | Type | Required | Description |
|---|---|---|---|
items | () => ToastItem[] | yes | The toasts currently shown (reactive). |
push | (input: ToastInput) => string | yes | Shows a toast; returns its id. |
dismiss | (id: string) => void | yes | Closes a toast by id. |
pause | (id: string) => void | yes | Pause / resume auto-dismiss (hover, focus). |
resume | (id: string) => void | yes | Restarts a paused toast's auto-dismiss timer. |
clear | () => void | yes | Closes every toast. |
ToastItem
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Toast id (use it with dismiss). |
title | string | Bold first line. | |
message | string | yes | Toast text. |
tone | AlertTone | Colour and icon. | |
action | ToastAction | Optional action button, e.g. "Undo" or "Retry". | |
state | "open" | "closed" | yes | closed while the exit animation plays. |
AlertTone
type AlertTone = "info" | "success" | "warning" | "danger";ToastAction
| Field | Type | Required | Description |
|---|---|---|---|
label | string | yes | Action button text. |
onClick | () => void | yes | Called when the action is pressed (the toast then closes). |
ToastInput
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Id to reuse (pushing the same id replaces that toast); generated when omitted. | |
title | string | Bold first line. | |
message | string | yes | Toast text. |
tone | AlertTone | Colour and icon. | |
action | ToastAction | Optional action button, e.g. "Undo" or "Retry". | |
durationMs | number | Auto-dismiss delay; 0 keeps the toast until dismissed. Default 4000. |
ToastPosition
type ToastPosition = | "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Message #
Colored message block for longer contextual notes.
function Example() {
const visible = signal(true);
return (
<Show
when={visible()}
fallback={
<Button size="sm" variant="outline" onClick={() => visible.set(true)}>
Show message
</Button>
}
>
<Message tone="info">
<MessageHeader onClose={() => visible.set(false)}>Scheduled maintenance</MessageHeader>
<MessageBody>
Builds pause on Sunday 02:00–03:00 UTC while we upgrade the runners.
</MessageBody>
</Message>
</Show>
);
}| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | content | MessageHeader and MessageBody, or body text. | ||
tone | MessageTone | Colour of the message. |
MessageTone
type MessageTone = "info" | "success" | "warning" | "danger" | "muted";Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Parts
MessageHeader #
Message title bar, with an optional close button.
function Example() {
const visible = signal(true);
return (
<Show
when={visible()}
fallback={
<Button size="sm" variant="outline" onClick={() => visible.set(true)}>
Show message
</Button>
}
>
<Message tone="warning">
<MessageHeader onClose={() => visible.set(false)}>
Usage limit
</MessageHeader>
<MessageBody>You've used 92% of this month's build minutes.</MessageBody>
</Message>
</Show>
);
}Slots: root close text
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | content | Header text. | ||
closeLabel | string | "Close" | Accessible label for the close button (default "Close"). | |
onClose | () => void | Shows a close button; called when it is pressed. |
MessageBody #
Body text of a message.
<Message tone="success">
<MessageBody>Your domain is verified.</MessageBody>
</Message>| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | content | Content of this part. |
Banners #
Full-width announcements.
Banner #
Full-width page banner.
function Example() {
const visible = signal(true);
return (
<Show
when={visible()}
fallback={
<Button size="sm" variant="outline" onClick={() => visible.set(true)}>
Show banner
</Button>
}
>
<Banner
tone="warning"
title="Payment failed"
action={
<Button size="sm" onClick={() => visible.set(false)}>
Update card
</Button>
}
onClose={() => visible.set(false)}
>
Your card ending in 4242 was declined.
</Banner>
</Show>
);
}Slots: root action body close content title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
action | content | Call to action at the end, e.g. a Button. | ||
children | content | Banner text. | ||
onClose | () => void | Shows a close button; called when it is pressed. | ||
title | string | Bold first line. | ||
tone | AlertTone | Colour and icon. |
AlertTone
type AlertTone = "info" | "success" | "warning" | "danger";AnnouncementBar #
Site-wide notice strip.
function Example() {
const visible = signal(true);
return (
<Show
when={visible()}
fallback={
<Button size="sm" variant="outline" onClick={() => visible.set(true)}>
Show announcement again
</Button>
}
>
<AnnouncementBar tone="accent" dismissible onDismiss={() => visible.set(false)}>
Arachne 2.4 is out — dark theme and a new component reference.
</AnnouncementBar>
</Show>
);
}Slots: root body close
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | content | Announcement text or content. | ||
dismissible | boolean | Show a dismiss (×) button. | ||
onDismiss | () => void | Called when the dismiss button is pressed; hide the bar here. | ||
tone | "accent" | "warning" | "info" | "danger" | "info" | Bar colour. |
UpgradeBanner #
Upsell banner with a call to action.
<UpgradeBanner
title="You're at 92% of your build minutes"
action={<Button onClick={() => {}}>Upgrade</Button>}
>
Pro includes 10,000 minutes and concurrent builds.
</UpgradeBanner>Slots: root action body content title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
action | content | Upgrade call to action, e.g. a Button. | ||
children | content | Pitch text. | ||
title | string | Headline. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Consent & offline notices #
Persistent notices about the session.
CookieConsent #
Cookie consent banner with accept / decline.
function Example() {
const choice = signal<"accepted" | "declined" | null>(null);
return (
<>
<CookieConsent
open={choice() === null}
message="We use cookies to keep you signed in and to measure usage."
onAccept={() => choice.set("accepted")}
onDecline={() => choice.set("declined")}
/>
<Show when={choice()}>
<Group gap="0.75rem">
<Text muted>Cookies {choice()}.</Text>
<Button size="sm" variant="outline" onClick={() => choice.set(null)}>
Ask again
</Button>
</Group>
</Show>
</>
);
}Slots: root actions body message title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
onAccept | () => void | yes | Called when the user accepts. | |
open | boolean | yes | Whether the notice is shown (controlled); hide it once the user chooses. | |
acceptLabel | string | "Accept" | Accept button text. | |
declineLabel | string | "Decline" | Decline button text. | |
message | string | "We use cookies to improve your experience. You can accept or decline non-essential cookies." | Explanation of what the cookies are for. | |
onDecline | () => void | Shows a decline button; called when it is pressed. | ||
title | string | "Cookies" | Bold first line. |
OfflineNotice #
Banner shown while the browser is offline.
<OfflineNotice offline />| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | content | "You are offline. Changes may not sync." | Message shown while offline. | |
offline | boolean | Force visibility (otherwise listens to navigator.onLine). |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.
Empty & error states #
What to show when there is nothing, or something failed.
EmptyState #
Placeholder for empty lists.
No projects yet
Create a project to start deploying.
<EmptyState
title="No projects yet"
description="Create a project to start deploying."
action={<Button onClick={() => {}}>New project</Button>}
/>Slots: root action description title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | yes | Headline, e.g. "No projects yet". | |
action | content | Call to action, e.g. a Button. | ||
children | content | Extra content below the description. | ||
description | content | Explanation under the title. |
NoResults #
Empty search results state.
No results for “kubernetes”
Try a shorter query or clear the filters.<NoResults query="kubernetes">
Try a shorter query or clear the filters.
</NoResults>Slots: root icon title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | content | Suggestions, e.g. how to broaden the search. | ||
query | string | The search that found nothing (quoted in the message). | ||
title | string | Override the title text. |
ErrorState #
Error state with description and a recovery action.
Something went wrong
We couldn't load your deploys. Check your connection and try again.
<ErrorState
description="We couldn't load your deploys. Check your connection and try again."
action={
<Button variant="outline" onClick={() => {}}>
Retry
</Button>
}
/>Slots: root action description icon title
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
action | content | Recovery action, e.g. a Retry Button. | ||
description | string | What went wrong and what to do. | ||
title | string | "Something went wrong" | Headline. |
Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.