UI componentsdocs/ui/components/feedback.md

Feedback

Tell the user what happened: alerts, notifications, banners and empty or error states.

← Component reference

Alert & callout #

Inline messages in the page.

Alert #

Inline status message; danger/warning are assertive alerts.

Deploy finished
marketing-site is live on production.
<Alert tone="success" title="Deploy finished">
    marketing-site is live on production.
</Alert>

Slots: root body title

PropTypeRequiredDefaultDescription
childrencontentMessage text or content.
titlestringBold first line.
toneAlertTone"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

PropTypeRequiredDefaultDescription
childrencontentNote text or content.
iconcontentReplace the tone icon (null hides it).
titlestringBold first line.
toneCalloutTone"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

Chrome on macOS, Berlin — just now.
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

PropTypeRequiredDefaultDescription
childrencontentNotification text.
onClose() => voidShows a close button; called when it is pressed.
titlestringBold first line.
toneAlertTone"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

PropTypeRequiredDefaultDescription
toasterToasteryesThe queue to render, from createToaster().
positionToastPosition"bottom-right"Screen corner or edge the toasts stack from.
render(toast: ToastItem, dismiss: () => void) => unknownCustom toast body; receives the item and a dismiss callback.

Toaster

FieldTypeRequiredDescription
items() => ToastItem[]yesThe toasts currently shown (reactive).
push(input: ToastInput) => stringyesShows a toast; returns its id.
dismiss(id: string) => voidyesCloses a toast by id.
pause(id: string) => voidyesPause / resume auto-dismiss (hover, focus).
resume(id: string) => voidyesRestarts a paused toast's auto-dismiss timer.
clear() => voidyesCloses every toast.

ToastItem

FieldTypeRequiredDescription
idstringyesToast id (use it with dismiss).
titlestringBold first line.
messagestringyesToast text.
toneAlertToneColour and icon.
actionToastActionOptional action button, e.g. "Undo" or "Retry".
state"open" | "closed"yesclosed while the exit animation plays.

AlertTone

type AlertTone = "info" | "success" | "warning" | "danger";

ToastAction

FieldTypeRequiredDescription
labelstringyesAction button text.
onClick() => voidyesCalled when the action is pressed (the toast then closes).

ToastInput

FieldTypeRequiredDescription
idstringId to reuse (pushing the same id replaces that toast); generated when omitted.
titlestringBold first line.
messagestringyesToast text.
toneAlertToneColour and icon.
actionToastActionOptional action button, e.g. "Undo" or "Retry".
durationMsnumberAuto-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.

Scheduled maintenance
Builds pause on Sunday 02:00–03:00 UTC while we upgrade the runners.
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>
    );
}
PropTypeRequiredDefaultDescription
childrencontentMessageHeader and MessageBody, or body text.
toneMessageToneColour 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.

Usage limit
You've used 92% of this month's build minutes.
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

PropTypeRequiredDefaultDescription
childrencontentHeader text.
closeLabelstring"Close"Accessible label for the close button (default "Close").
onClose() => voidShows a close button; called when it is pressed.

MessageBody #

Body text of a message.

Your domain is verified.
<Message tone="success">
    <MessageBody>Your domain is verified.</MessageBody>
</Message>
PropTypeRequiredDefaultDescription
childrencontentContent of this part.

Banners #

Full-width announcements.

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

PropTypeRequiredDefaultDescription
actioncontentCall to action at the end, e.g. a Button.
childrencontentBanner text.
onClose() => voidShows a close button; called when it is pressed.
titlestringBold first line.
toneAlertToneColour and icon.

AlertTone

type AlertTone = "info" | "success" | "warning" | "danger";

AnnouncementBar #

Site-wide notice strip.

Arachne 2.4 is out — dark theme and a new component reference.
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

PropTypeRequiredDefaultDescription
childrencontentAnnouncement text or content.
dismissiblebooleanShow a dismiss (×) button.
onDismiss() => voidCalled 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.

You're at 92% of your build minutes
Pro includes 10,000 minutes and concurrent builds.
Interact with the example: callbacks show up here.
<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

PropTypeRequiredDefaultDescription
actioncontentUpgrade call to action, e.g. a Button.
childrencontentPitch text.
titlestringHeadline.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.

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

PropTypeRequiredDefaultDescription
onAccept() => voidyesCalled when the user accepts.
openbooleanyesWhether the notice is shown (controlled); hide it once the user chooses.
acceptLabelstring"Accept"Accept button text.
declineLabelstring"Decline"Decline button text.
messagestring"We use cookies to improve your experience. You can accept or decline non-essential cookies."Explanation of what the cookies are for.
onDecline() => voidShows a decline button; called when it is pressed.
titlestring"Cookies"Bold first line.

OfflineNotice #

Banner shown while the browser is offline.

<OfflineNotice offline />
PropTypeRequiredDefaultDescription
childrencontent"You are offline. Changes may not sync."Message shown while offline.
offlinebooleanForce 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.

Interact with the example: callbacks show up here.
<EmptyState
    title="No projects yet"
    description="Create a project to start deploying."
    action={<Button onClick={() => {}}>New project</Button>}
/>

Slots: root action description title

PropTypeRequiredDefaultDescription
titlestringyesHeadline, e.g. "No projects yet".
actioncontentCall to action, e.g. a Button.
childrencontentExtra content below the description.
descriptioncontentExplanation 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

PropTypeRequiredDefaultDescription
childrencontentSuggestions, e.g. how to broaden the search.
querystringThe search that found nothing (quoted in the message).
titlestringOverride the title text.

ErrorState #

Error state with description and a recovery action.

Interact with the example: callbacks show up here.
<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

PropTypeRequiredDefaultDescription
actioncontentRecovery action, e.g. a Retry Button.
descriptionstringWhat went wrong and what to do.
titlestring"Something went wrong"Headline.

Also accepts the shared props: pass-through attributes, class, style, classes, styles, unstyled.