Alert

A versatile alert component for displaying important messages to users. Supports multiple variants (info, success, warning, error), custom icons, optional titles, closable functionality, and banner mode. Perfect for notifications, status messages, and user feedback.

Install

#Import

use gpui_kit::component::alert::Alert;
// Installed in ui/components. That directory is on the Slint include path, so import by file name:
import { Alert } from "alert.slint";
use iced::widget::{column, container, mouse_area, row, scrollable, svg, text};
use iced::{Alignment, Color, Element, Font, Length, Task, Theme, font};

#Usage

#Default

Alert::new("alert-id", "This is a basic alert message.")
Alert {
    title: "Workspace settings saved";
    message: "Your workspace is ready for the team.\n• 12 members have access\n• Billing remains with the workspace owner";
}
// The plain callout: a title and a three-line message in the info ink over
// the status colour's 4% tint. The leading glyph is the caller's, and the
// Slint default is the info tone.
let default_alert = body_box(
    Alert::new(
        "Your workspace is ready for the team.\n\
         • 12 members have access\n\
         • Billing remains with the workspace owner",
    )
    .title("Workspace settings saved")
    .icon(status_icon(Status::Info)),
);

#Variants

// Info alert (blue)
Alert::info("info-alert", "This is an informational message.")
    .title("Information")

// Success alert (green)
Alert::success("success-alert", "Your operation completed successfully.")
    .title("Success!")

// Warning alert (yellow/orange)
Alert::warning("warning-alert", "Please review your settings before proceeding.")
    .title("Warning")

// Error alert (red)
Alert::error("error-alert", "An error occurred while processing your request.")
    .title("Error")
Alert {
    title: "Scheduled maintenance";
    message: "Maintenance starts Friday at 22:00 UTC.";
    status: Status.info;
    closable: true;
}
Alert {
    title: "Transfer submitted";
    message: "The transfer is queued and usually settles within one business day.";
    status: Status.success;
}
Alert {
    message: "Two teammates still use recovery codes generated more than a year ago. Ask them to generate a fresh set in Security settings.";
    status: Status.warning;
}
Alert {
    title: "Unable to process your payment.";
    message: "Please verify your billing information and try again.\n• Check your card details\n• Ensure sufficient funds\n• Verify billing address";
    status: Status.danger;
}
// The four statuses in the Slint order. The info one is dismissible, so the
// application owns its flag and stops constructing it once it is closed; the
// other three take their fill, edge and ink from their own status.
let mut variants = column![].spacing(8.0);
if app.variant_notice_open {
    variants = variants.push(
        Alert::new("Maintenance starts Friday at 22:00 UTC.")
            .title("Scheduled maintenance")
            .status(Status::Info)
            .icon(status_icon(Status::Info))
            .on_close(Message::DismissVariantNotice),
    );
}
variants = variants.push(
    Alert::new("The transfer is queued and usually settles within one business day.")
        .title("Transfer submitted")
        .status(Status::Success)
        .icon(status_icon(Status::Success)),
);
variants = variants.push(
    Alert::new(
        "Two teammates still use recovery codes generated more than a year ago. Ask them \
         to generate a fresh set in Security settings.",
    )
    .status(Status::Warning)
    .icon(status_icon(Status::Warning)),
);
variants = variants.push(
    Alert::new(
        "Please verify your billing information and try again.\n\
         • Check your card details\n\
         • Ensure sufficient funds\n\
         • Verify billing address",
    )
    .title("Unable to process your payment.")
    .status(Status::Danger)
    .icon(status_icon(Status::Danger)),
);
let variants = body_box(variants);
GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The full-width bar: square corners, a tighter padding and no title. The
// first is dismissible and owns its own flag; the rest are the four statuses.
let mut banners = column![].spacing(8.0);
if app.banner_open {
    banners = banners.push(
        Alert::new("Reporting is read-only while the nightly ledger closes.")
            .icon(status_icon(Status::Info))
            .banner(true)
            .on_close(Message::DismissBanner),
    );
}
banners = banners.push(
    Alert::new("A new desktop update will install after you restart.")
        .status(Status::Info)
        .icon(status_icon(Status::Info))
        .banner(true),
);
banners = banners.push(
    Alert::new("All 1,284 records finished importing.")
        .status(Status::Success)
        .icon(status_icon(Status::Success))
        .banner(true),
);
banners = banners.push(
    Alert::new("Your API key expires in 6 days. Rotate it before August 19.")
        .status(Status::Warning)
        .icon(status_icon(Status::Warning))
        .banner(true),
);
banners = banners.push(
    Alert::new("Live updates are disconnected. Changes may be delayed.")
        .status(Status::Danger)
        .icon(status_icon(Status::Danger))
        .banner(true),
);
let banners = body_box(banners);

Banner alerts take full width and don’t display titles:

Alert::info("banner-alert", "This is a banner alert that spans the full width.")
    .banner()

Alert::success("banner-success", "Operation completed successfully!")
    .banner()

Alert::warning("banner-warning", "System maintenance scheduled for tonight.")
    .banner()

Alert::error("banner-error", "Service temporarily unavailable.")
    .banner()
VerticalLayout {
    spacing: 8px;
    alignment: start;
    Alert {
        message: "Reporting is read-only while the nightly ledger closes.";
        banner: true;
        closable: true;
        open <=> root.banner-open;
    }
    Alert {
        message: "A new desktop update will install after you restart.";
        status: Status.info;
        banner: true;
    }
    Alert {
        message: "All 1,284 records finished importing.";
        status: Status.success;
        banner: true;
    }
    Alert {
        message: "Your API key expires in 6 days. Rotate it before August 19.";
        status: Status.warning;
        banner: true;
    }
    Alert {
        message: "Live updates are disconnected. Changes may be delayed.";
        status: Status.danger;
        banner: true;
    }
}

#Custom icon

use gpui_kit::component::IconName;

Alert::new("custom-icon", "Meeting scheduled for tomorrow at 3 PM.")
    .title("Calendar Reminder")
    .icon(IconName::Calendar)
Alert {
    title: "Two events overlap by 30 minutes";
    message: "The quarterly planning review overlaps with the APAC operations call. Move one event or invite another owner before sending the agenda.";
    icon: "calendar";
}
// The leading slot takes any element, so the calendar artwork replaces the
// status glyph; the ink is still the status colour.
let custom_icon = body_box(
    Alert::new(
        "The quarterly planning review overlaps with the APAC operations call. Move one \
         event or invite another owner before sending the agenda.",
    )
    .title("Two events overlap by 30 minutes")
    .icon(glyph(CALENDAR_XML, Status::Info)),
);

#Alert with Title

Alert::new("alert-with-title", "Your changes have been saved successfully.")
    .title("Success!")
SlintNo Slint version of this example yet.

#Alert Sizes

use gpui_kit::component::{alert::Alert, Sizable as _};

Alert::info("alert", "Message content")
    .xsmall()
    .title("XSmall Alert")
Alert::info("alert", "Message content")
    .small()
    .title("Small Alert")

Alert::info("alert", "Message content")
    .title("Medium Alert")

Alert::info("alert", "Message content")
    .large()
    .title("Large Alert")
Alert {
    title: "XSmall Alert";
    message: "Message content";
    status: Status.info;
    size: ControlSize.xsmall;
}
Alert {
    title: "Small Alert";
    message: "Message content";
    status: Status.info;
    size: ControlSize.small;
}
Alert {
    title: "Medium Alert";
    message: "Message content";
    status: Status.info;
}
Alert {
    title: "Large Alert";
    message: "Message content";
    status: Status.info;
    size: ControlSize.large;
}
// The size ladder changes the padding, the gap and the corner only; the
// message stays at text-sm at every step, as GPUI's does.
let sizes = body_box(
    column![
        Alert::new("Message content")
            .title("XSmall Alert")
            .status(Status::Info)
            .size(ControlSize::XSmall)
            .icon(status_icon(Status::Info)),
        Alert::new("Message content")
            .title("Small Alert")
            .status(Status::Info)
            .size(ControlSize::Small)
            .icon(status_icon(Status::Info)),
        Alert::new("Message content")
            .title("Medium Alert")
            .status(Status::Info)
            .icon(status_icon(Status::Info)),
        Alert::new("Message content")
            .title("Large Alert")
            .status(Status::Info)
            .size(ControlSize::Large)
            .icon(status_icon(Status::Info)),
    ]
    .spacing(12.0),
);

#Closable Alerts

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The dismiss control appears only when the caller passes the message it
// sends: an iced alert owns no `open` flag, so the application closes it.
let mut closable = column![].spacing(8.0);
if app.dismissible_open {
    closable = closable.push(
        Alert::new("This alert can be dismissed.")
            .title("Dismissible")
            .status(Status::Info)
            .icon(status_icon(Status::Info))
            .on_close(Message::DismissClosable),
    );
}
closable = closable.push(muted_line(
    "Pressed, the close control sends its message and the application stops drawing the alert.",
));
let closable = body_box(closable);

When you add an on_close handler, a close button appears on the alert:

Alert::info("closable-alert", "This alert can be dismissed.")
    .title("Dismissible")
    .on_close(|_event, _window, _cx| {
        println!("Alert was closed");
        // Handle alert dismissal
    })
Alert {
    title: "Dismissible";
    message: "This alert can be dismissed.";
    status: Status.info;
    closable: true;
}

#With Markdown Content

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// Slint fills the alert's `@children` with a formatted `TextView`; an iced
// alert carries a plain message, so this is the same words with the list's
// own line breaks, and the formatting is the caller's to place around it.
let markdown = body_box(
    Alert::new(
        "Please verify your billing information and try again.\n\
         • Check your card details\n\
         • Ensure sufficient funds\n\
         • Verify billing address",
    )
    .title("Payment Failed")
    .status(Status::Danger)
    .icon(status_icon(Status::Danger)),
);

We can use TextView to render formatted (Markdown or HTML) text within the alert, for displaying lists, bold text, links, etc.

use gpui_kit::component::text::markdown;

Alert::error(
    "error-with-markdown",
    markdown(
        "Please verify your billing information and try again.\n\
        - Check your card details\n\
        - Ensure sufficient funds\n\
        - Verify billing address"
    ),
)
.title("Payment Failed")
Alert {
    title: "Payment Failed";
    status: Status.danger;
    TextView {
        TextParagraph {
            text: @markdown("Please verify your billing information and try again.");
            default-color: Theme.danger;
        }
        TextParagraph {
            text: @markdown("• Check your card details");
            default-color: Theme.danger;
        }
        TextParagraph {
            text: @markdown("• Ensure sufficient funds");
            default-color: Theme.danger;
        }
        TextParagraph {
            text: @markdown("• Verify billing address");
            default-color: Theme.danger;
        }
    }
}

#Conditional Visibility

Alert::info("conditional-alert", "This alert may be hidden.")
    .title("Conditional")
    .visible(should_show_alert) // boolean condition
Switch {
    label: "Show alert";
    checked <=> root.should-show-alert;
}
Alert {
    title: "Conditional";
    message: "This alert may be hidden.";
    status: Status.info;
    open: root.should-show-alert;
}
// The switch and the alert share one flag: the caller owns it and simply
// stops constructing the alert, which is how iced spells `open`.
let mut conditional = column![
    Switch::new(app.should_show_alert)
        .label("Show alert")
        .on_toggle(Message::ShowAlert),
]
.spacing(8.0);
if app.should_show_alert {
    conditional = conditional.push(
        Alert::new("This alert may be hidden.")
            .title("Conditional")
            .status(Status::Info)
            .icon(status_icon(Status::Info)),
    );
}
let conditional = body_box(conditional);

#Form Validation Errors

Alert::error(
    "validation-error",
    "Please correct the following errors before submitting:\n\
    - Email address is required\n\
    - Password must be at least 8 characters\n\
    - Terms of service must be accepted"
)
.title("Validation Failed")
SlintNo Slint version of this example yet.

#Success Notification

Alert::success("save-success", "Your profile has been updated successfully.")
    .title("Changes Saved")
    .on_close(|_, _, _| {
        // Auto-dismiss after showing
    })
SlintNo Slint version of this example yet.

#System Status Banner

Alert::warning(
    "maintenance-banner",
    "Scheduled maintenance will occur tonight from 2:00 AM to 4:00 AM EST. \
    Some services may be temporarily unavailable."
)
.banner()
.large()
SlintNo Slint version of this example yet.

#Interactive Alert with Custom Action

Alert::info("update-available", "A new version of the application is available.")
    .title("Update Available")
    .icon(IconName::Download)
    .on_close(cx.listener(|this, _, _, cx| {
        // Handle update or dismiss
        this.handle_update_notification(cx);
    }))
Alert {
    title: "Update Available";
    message: "A new version of the application is available.";
    status: Status.info;
    icon: "download";
    closable: true;
    closed => {
        root.update-dismissed = true;
    }
}
if root.update-dismissed: Text {
    text: "Update dismissed";
    color: Theme.muted-foreground;
    font-size: Theme.font-size-sm;
}
// The close control reports the dismissal to the application instead of
// hiding anything itself, so the demo writes the line under it.
let mut interactive = column![].spacing(8.0);
if app.update_dismissed {
    interactive = interactive.push(muted_line("Update dismissed"));
} else {
    interactive = interactive.push(
        Alert::new("A new version of the application is available.")
            .title("Update Available")
            .status(Status::Info)
            .icon(glyph(DOWNLOAD_XML, Status::Info))
            .on_close(Message::DismissUpdate),
    );
}
let interactive = body_box(interactive);

#Multi-line Content with Formatting

use gpui_kit::component::text::markdown;

Alert::warning(
    "security-alert",
    markdown(
        "**Security Notice**: Unusual activity detected on your account.\n\n\
        Recent activity:\n\
        - Login from new device (Chrome on Windows)\n\
        - Location: San Francisco, CA\n\
        - Time: Today at 2:30 PM\n\n\
        If this wasn't you, please [change your password](/) immediately."
    )
)
.title("Security Alert")
.icon(IconName::Shield)
Alert {
    title: "Security Alert";
    status: Status.warning;
    icon: "shield";
    TextView {
        TextHeading {
            level: 4;
            text: "Security Notice";
            color: Theme.warning;
        }
        TextParagraph {
            text: @markdown("Unusual activity detected on your account.");
            default-color: Theme.warning;
        }
        TextParagraph {
            text: @markdown("Recent activity:");
            default-color: Theme.warning;
        }
        TextParagraph {
            text: @markdown("• Login from new device (Chrome on Windows)");
            default-color: Theme.warning;
        }
        TextParagraph {
            text: @markdown("• Location: San Francisco, CA");
            default-color: Theme.warning;
        }
        TextParagraph {
            text: @markdown("• Time: Today at 2:30 PM");
            default-color: Theme.warning;
        }
        TextParagraph {
            text: @markdown("If this wasn't you, please change your password immediately.");
            default-color: Theme.warning;
        }
    }
}
// The second formatted body: Slint's `TextView` again, and here the same
// words as plain text in the warning ink behind the shield artwork.
let multiline = body_box(
    Alert::new(
        "Security Notice\n\n\
         Unusual activity detected on your account.\n\n\
         Recent activity:\n\
         • Login from new device (Chrome on Windows)\n\
         • Location: San Francisco, CA\n\
         • Time: Today at 2:30 PM\n\n\
         If this wasn't you, please change your password immediately.",
    )
    .title("Security Alert")
    .status(Status::Warning)
    .icon(glyph(SHIELD_XML, Status::Warning)),
);

#API Reference

#Alert

A callout for user attention. Mirrors GPUI Component's `Alert`: status variants, an optional title, sizes, a closable banner, and custom icons. `open` is the visibility flag. Formatted body content goes in `@children`.

NameKindTypeDefault
titleinstring
messageinstring
statusinStatusStatus.info
iconinstring
closableinbool
bannerinbool
sizeinControlSizeControlSize.medium
openin-outbooltrue
closedcallback()