Badge

A versatile badge component that can display counts, dots, or icons on elements. Perfect for indicating notifications, status, or other contextual information on avatars, icons, or other UI elements.

Install

#Import

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

#Usage

#Icon

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// A count on an icon: 3 reads as it is, 103 rolls over the default maximum
// of 99 and reads `99+`.
let icon = body_box(
    row![
        Badge::new(icon_mark(BELL, 24.0)).count(3),
        Badge::new(icon_mark(INBOX, 24.0)).count(103),
    ]
    .spacing(24.0)
    .align_y(Alignment::Center),
);
  • Default: Displays a numeric count.
  • Dot: A small dot indicator, typically used for status.
  • Icon: Displays an icon instead of a number.
// Number badge (default)
Badge::new()
    .count(5)
    .child(Avatar::new().src("https://example.com/avatar.jpg"))

// Dot badge
Badge::new()
    .dot()
    .child(Icon::new(IconName::Inbox))

// Icon badge
Badge::new()
    .icon(IconName::Check)
    .child(Avatar::new().src("https://example.com/avatar.jpg"))
GridLayout {
    spacing: 24px;
    Badge {
        horizontal-stretch: 0;
        count: 3;
        Icon {
            name: "bell";
            size: 24px;
            color: Theme.foreground;
        }
    }
    Badge {
        horizontal-stretch: 0;
        count: 103;
        Icon {
            name: "inbox";
            size: 24px;
            color: Theme.foreground;
        }
    }
}

#Count

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The same count on an avatar, which is the shape the component page
// documents this sample with.
let count = body_box(
    row![
        Badge::new(avatar_mark("AL")).count(3),
        Badge::new(avatar_mark("MK")).count(103),
    ]
    .spacing(24.0)
    .align_y(Alignment::Center),
);

Use count to display a numeric badge, if the count is greater than zero (> 0) the badge will be shown, otherwise it will be hidden.

There is a default maximum count of 99, any count above this will be displayed as 99+. You can customize this maximum using the max method.

Badge::new()
    .count(3)
    .child(Icon::new(IconName::Bell))
GridLayout {
    spacing: 24px;
    Badge {
        horizontal-stretch: 0;
        count: 3;
        Avatar {
            initials: "AL";
        }
    }
    Badge {
        horizontal-stretch: 0;
        count: 103;
        Avatar {
            initials: "MK";
            fill: Theme.muted;
        }
    }
}

#Badge icon

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// A status icon in a round chip instead of a number, in the semantic colours
// the Slint example names: info for the check, warning for the star.
let badge_icon = body_box(
    row![
        Badge::new(avatar_mark("AL"))
            .icon(chip_mark(CHECK, app.tokens.primary_foreground))
            .color(app.tokens.info),
        Badge::new(avatar_mark("JR"))
            .icon(chip_mark(STAR, app.tokens.primary_foreground))
            .color(app.tokens.warning),
    ]
    .spacing(24.0)
    .align_y(Alignment::Center),
);

The Badge is also implemented with the Sizable trait, allowing you to set small, medium (default), or large sizes.

// Small badge
Badge::new()
    .small()
    .count(1)
    .child(Avatar::new().small())

// Medium badge (default)
Badge::new()
    .count(5)
    .child(Avatar::new())

// Large badge
Badge::new()
    .large()
    .count(10)
    .child(Avatar::new().large())
GridLayout {
    spacing: 24px;
    Badge {
        horizontal-stretch: 0;
        icon: "check";
        color: Theme.info;
        Avatar {
            initials: "AL";
        }
    }
    Badge {
        horizontal-stretch: 0;
        icon: "star";
        color: Theme.warning;
        Avatar {
            initials: "JR";
        }
    }
}

#Dot

use gpui_kit::component::ActiveTheme;

// Custom colors
Badge::new()
    .count(3)
    .color(cx.theme().blue)
    .child(Avatar::new())

Badge::new()
    .icon(IconName::Star)
    .color(cx.theme().yellow)
    .child(Avatar::new())

Badge::new()
    .dot()
    .color(cx.theme().green)
    .child(Icon::new(IconName::Bell))
HorizontalLayout {
    spacing: 24px;
    alignment: start;
    Badge {
        dot: true;
        count: 1;
        Avatar {
            initials: "AL";
        }
    }
}
// A six-pixel dot in place of the count, for a status rather than a number.
let dot = body_box(
    row![Badge::new(avatar_mark("AL")).dot().count(1)]
        .spacing(24.0)
        .align_y(Alignment::Center),
);

#Color

use gpui_kit::component::{Icon, IconName};

// Badge with count on icon
Badge::new()
    .count(3)
    .child(Icon::new(IconName::Bell).large())

// Badge with high count (shows max)
Badge::new()
    .count(103)
    .child(Icon::new(IconName::Inbox).large())

// Custom max count
Badge::new()
    .count(150)
    .max(999)
    .child(Icon::new(IconName::Mail))
GridLayout {
    spacing: 24px;
    Badge {
        horizontal-stretch: 0;
        count: 3;
        color: Theme.selection;
        Avatar {
            initials: "AL";
        }
    }
    Badge {
        horizontal-stretch: 0;
        dot: true;
        count: 1;
        color: Theme.success;
        Avatar {
            initials: "AL";
        }
    }
}
// A caller-supplied fill: `selection` for the count, `success` for the dot.
let color = body_box(
    row![
        Badge::new(avatar_mark("AL"))
            .count(3)
            .color(app.tokens.selection),
        Badge::new(avatar_mark("AL"))
            .dot()
            .count(1)
            .color(app.tokens.success),
    ]
    .spacing(24.0)
    .align_y(Alignment::Center),
);

#Sizes

use gpui_kit::component::avatar::Avatar;

// Basic count badge
Badge::new()
    .count(5)
    .child(Avatar::new().src("https://example.com/avatar.jpg"))

// Status badge with icon
Badge::new()
    .icon(IconName::Check)
    .color(cx.theme().green)
    .child(Avatar::new().src("https://example.com/avatar.jpg"))

// Online indicator with dot
Badge::new()
    .dot()
    .color(cx.theme().green)
    .child(Avatar::new().src("https://example.com/avatar.jpg"))
GridLayout {
    spacing: 24px;
    Badge {
        horizontal-stretch: 0;
        count: 2;
        size: ControlSize.small;
        Icon {
            name: "bell";
            size: 16px;
            color: Theme.foreground;
        }
    }
    Badge {
        horizontal-stretch: 0;
        count: 2;
        Icon {
            name: "bell";
            size: 24px;
            color: Theme.foreground;
        }
    }
    Badge {
        horizontal-stretch: 0;
        count: 2;
        size: ControlSize.large;
        Icon {
            name: "bell";
            size: 32px;
            color: Theme.foreground;
        }
    }
}
// The size ladder, with the icons the Slint example pairs it with: small at
// 16 pixels, the default at 24, large at 32.
let sizes = body_box(
    row![
        Badge::new(icon_mark(BELL, 16.0))
            .count(2)
            .size(ControlSize::Small),
        Badge::new(icon_mark(BELL, 24.0))
            .count(2)
            .size(ControlSize::Medium),
        Badge::new(icon_mark(BELL, 32.0))
            .count(2)
            .size(ControlSize::Large),
    ]
    .spacing(24.0)
    .align_y(Alignment::Center),
);

#Complex Nested Badges

// Badge on badge for complex status
Badge::new()
    .count(212)
    .large()
    .child(
        Badge::new()
            .icon(IconName::Check)
            .large()
            .color(cx.theme().cyan)
            .child(Avatar::new().large().src("https://example.com/avatar.jpg"))
    )

// Multiple status indicators
Badge::new()
    .count(2)
    .color(cx.theme().green)
    .large()
    .child(
        Badge::new()
            .icon(IconName::Star)
            .large()
            .color(cx.theme().yellow)
            .child(Avatar::new().large().src("https://example.com/avatar.jpg"))
    )
SlintNo Slint version of this example yet.

#Notification Indicators

// Unread messages
Badge::new()
    .count(12)
    .child(Icon::new(IconName::Mail).large())

// New notifications
Badge::new()
    .count(3)
    .color(cx.theme().red)
    .child(Icon::new(IconName::Bell).large())

// High priority with custom max
Badge::new()
    .count(1234)
    .max(999)
    .color(cx.theme().orange)
    .child(Icon::new(IconName::AlertTriangle))
SlintNo Slint version of this example yet.

#Status Indicators

// Online status
Badge::new()
    .dot()
    .color(cx.theme().green)
    .child(Avatar::new().src("https://example.com/user.jpg"))

// Verified status
Badge::new()
    .icon(IconName::CheckCircle)
    .color(cx.theme().blue)
    .child(Avatar::new().src("https://example.com/verified-user.jpg"))

// Warning status
Badge::new()
    .icon(IconName::AlertTriangle)
    .color(cx.theme().yellow)
    .child(Avatar::new().src("https://example.com/user.jpg"))
SlintNo Slint version of this example yet.

#Different Badge Positions

// The badge automatically positions itself based on variant:
// - Dot: top-right corner (small dot)
// - Number: top-right with dynamic sizing
// - Icon: bottom-right corner with border
SlintNo Slint version of this example yet.

#Count Formatting

// Numbers 1-99 show as-is
Badge::new().count(5)    // Shows "5"
Badge::new().count(99)   // Shows "99"

// Numbers above max show with "+"
Badge::new().count(100)  // Shows "99+" (default max)
Badge::new().count(1000).max(999) // Shows "999+"

// Zero count hides the badge
Badge::new().count(0)    // Badge not visible
SlintNo Slint version of this example yet.

#API Reference

#Badge

An overlay on an icon or avatar. Mirrors GPUI Component's `Badge`: a count, a dot, or a small status icon anchored to the corner of its child.

NameKindTypeDefault
countinint
maxinint99
iconinstring
colorincolorTheme.danger
dotinbool
sizeinControlSizeControlSize.medium