GroupBox

The GroupBox component is a versatile container that groups related content together with optional borders, backgrounds, and titles. It provides visual organization and semantic grouping for form controls, settings panels, and other related UI elements.

Install

#Import

use gpui_kit::component::group_box::{GroupBox, GroupBoxVariant, GroupBoxVariants as _};
// Installed in ui/components. That directory is on the Slint include path, so import by file name:
import { GroupBoxVariant, GroupBox } from "group-box.slint";
use iced::widget::{column, container, mouse_area, row, scrollable, svg, text};
use iced::{Alignment, Element, Length, Task, Theme};

#Usage

#Account Settings

GroupBox::new()
    .outline()
    .title("Display Settings")
    .child(
        v_flex()
            .gap_3()
            .child(
                h_flex()
                    .justify_between()
                    .child(Label::new("Theme"))
                    .child(
                        RadioGroup::horizontal("theme")
                            .child(Radio::new("light").label("Light"))
                            .child(Radio::new("dark").label("Dark"))
                            .child(Radio::new("auto").label("Auto"))
                    )
            )
            .child(
                h_flex()
                    .justify_between()
                    .child(Label::new("Font Size"))
                    .child(
                        Select::new("font-size")
                            .option("small", "Small")
                            .option("medium", "Medium")
                            .option("large", "Large")
                    )
            )
    )
HorizontalLayout {
    horizontal-stretch: 1;
    spacing: 12px;
    Text {
        horizontal-stretch: 1;
        horizontal-alignment: left;
        text: "Make profile private and hide activity";
        color: Theme.foreground;
        font-size: Theme.font-size-sm;
        vertical-alignment: center;
    }
    Switch {
        checked: true;
    }
}
HorizontalLayout {
    horizontal-stretch: 1;
    spacing: 12px;
    Text {
        horizontal-stretch: 1;
        horizontal-alignment: left;
        text: "Include private contributions on my profile";
        color: Theme.foreground;
        font-size: Theme.font-size-sm;
        vertical-alignment: center;
    }
    Switch {
        checked: false;
    }
}
HorizontalLayout {
    alignment: start;
    Button {
        text: "Save Changes";
        variant: ButtonVariant.primary;
    }
}
// A filled group, its label taking the room and the switch at the trailing
// edge, then the primary action: the Slint example's first `GroupBox`.
let account = body_box(
    GroupBox::new()
        .title("Account Settings")
        .variant(group_box::Variant::Fill)
        .body(
            column![
                setting(
                    "Make profile private and hide activity",
                    Switch::new(app.private).on_toggle(Message::Private),
                ),
                setting(
                    "Include private contributions on my profile",
                    Switch::new(app.contributions).on_toggle(Message::Contributions),
                ),
                Button::new("Save Changes")
                    .variant(Variant::Primary)
                    .on_press(Message::Nothing),
            ]
            .spacing(theme::SPACING_LG),
        ),
);

#Preferences

GroupBox::new()
    .child("Subscriptions")
    .child(Checkbox::new("all").label("All"))
    .child(Checkbox::new("newsletter").label("Newsletter"))
    .child(Button::new("save").primary().label("Save"))
Checkbox {
    label: "All activity";
    description: "Every mention, reply, and review.";
    checked: true;
}
Checkbox {
    label: "Product updates";
    description: "Release notes and deprecations.";
    checked: true;
}
Checkbox {
    label: "Account activity";
    description: "Sign-ins and billing changes.";
}
// Three checkboxes, each with the description the Slint example writes under
// its label.
let preferences = body_box(
    column![
        Checkbox::new(app.all_activity)
            .label("All activity")
            .description("Every mention, reply, and review.")
            .on_toggle(Message::AllActivity),
        Checkbox::new(app.product_updates)
            .label("Product updates")
            .description("Release notes and deprecations.")
            .on_toggle(Message::ProductUpdates),
        Checkbox::new(app.account_activity)
            .label("Account activity")
            .description("Sign-ins and billing changes.")
            .on_toggle(Message::AccountActivity),
    ]
    .spacing(theme::SPACING_LG),
);

#GroupBox Variants

// Normal variant (default) - no background or border
GroupBox::new()
    .child("Content without visual container")

// Fill variant - with background color
GroupBox::new()
    .fill()
    .title("Settings")
    .child("Content with background")

// Outline variant - with border, no background
GroupBox::new()
    .outline()
    .title("Preferences")
    .child("Content with border")
HorizontalLayout {
    horizontal-stretch: 1;
    spacing: 12px;
    Panel {
        horizontal-stretch: 1;
        Text {
            text: "No visual container. The title alone groups the content.";
            color: Theme.foreground;
            font-size: Theme.font-size-sm;
            wrap: word-wrap;
        }
    }
    Panel {
        horizontal-stretch: 1;
        Text {
            text: "A muted background, for a recessed settings surface.";
            color: Theme.foreground;
            font-size: Theme.font-size-sm;
            wrap: word-wrap;
        }
    }
    Panel {
        horizontal-stretch: 1;
        variant: GroupBoxVariant.outline;
        Text {
            text: "A one-pixel border, for a card that must read as a unit.";
            color: Theme.foreground;
            font-size: Theme.font-size-sm;
            wrap: word-wrap;
        }
    }
}
// The three surfaces side by side, each on its own rather than nested.
let variants = body_box(
    row![
        variant_panel(
            group_box::Variant::Normal,
            "No visual container. The title alone groups the content.",
        ),
        variant_panel(
            group_box::Variant::Fill,
            "A muted background, for a recessed settings surface.",
        ),
        variant_panel(
            group_box::Variant::Outline,
            "A one-pixel border, for a card that must read as a unit.",
        ),
    ]
    .spacing(theme::SPACING_MD),
);

#With Title

GroupBox::new()
    .fill()
    .title("Account Settings")
    .child(
        h_flex()
            .justify_between()
            .child("Make profile private")
            .child(Switch::new("privacy").checked(false))
    )
    .child(Button::new("save").primary().label("Save Changes"))
Panel {
    title: "Email notifications";
    variant: GroupBoxVariant.fill;
    Checkbox {
        label: "All activity";
        checked: true;
    }
    Checkbox {
        label: "Account activity";
    }
    HorizontalLayout {
        alignment: start;
        Button {
            text: "Save preferences";
            variant: ButtonVariant.primary;
        }
    }
}
// The titled surface the group box is for: the title outside, the controls
// and the action inside a filled body.
let with_title = body_box(
    GroupBox::new()
        .title("Email notifications")
        .variant(group_box::Variant::Fill)
        .body(
            column![
                Checkbox::new(app.all_activity)
                    .label("All activity")
                    .on_toggle(Message::AllActivity),
                Checkbox::new(app.account_activity)
                    .label("Account activity")
                    .on_toggle(Message::AccountActivity),
                Button::new("Save preferences")
                    .variant(Variant::Primary)
                    .on_press(Message::Nothing),
            ]
            .spacing(theme::SPACING_LG),
        ),
);
GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// The footer hangs under the filled surface as small muted text, 8px away,
// sharing the title's leading edge.
let footer = body_box(
    GroupBox::new()
        .title("Contributions & activity")
        .variant(group_box::Variant::Fill)
        .footer("Private contributions never reveal repository names.")
        .body(setting(
            "Make profile private and hide activity",
            Switch::new(app.private).on_toggle(Message::Private),
        )),
);

Use footer for supporting content below the filled background or outline, not inside the content area. It shares the title’s leading edge, sits 8 px under the surface, and renders as small muted text like a description, so plain text is enough. content_style only changes the body.

GroupBox::new()
    .fill()
    .child("Update preferences")
    .footer("Changes apply to this device only.")
Panel {
    title: "Contributions & activity";
    variant: GroupBoxVariant.fill;
    footer: "Private contributions never reveal repository names.";
    HorizontalLayout {
        horizontal-stretch: 1;
        spacing: 12px;
        Text {
            horizontal-stretch: 1;
            horizontal-alignment: left;
            text: "Make profile private and hide activity";
            color: Theme.foreground;
            font-size: Theme.font-size-sm;
            vertical-alignment: center;
        }
        Switch {
            checked: true;
        }
    }
}

#Custom ID

GroupBox::new()
    .id("user-preferences")
    .outline()
    .title("User Preferences")
    .child("Preference controls...")
SlintNo Slint version of this example yet.

#Custom Title Styling

use gpui_kit::{StyleRefinement, relative};

GroupBox::new()
    .outline()
    .title("Custom Title")
    .title_style(
        StyleRefinement::default()
            .font_semibold()
            .line_height(relative(1.0))
            .px_3()
            .text_color(cx.theme().accent)
    )
    .child("Content with custom title styling")
Panel {
    title: "Appearance";
    variant: GroupBoxVariant.outline;
    title-color: Theme.foreground;
    RadioGroup {
        options: [
            "Light",
            "Dark",
            "System"
        ];
        selected: 2;
    }
}
// `title_color` takes any colour; the sample names the page's foreground,
// which is the colour the Slint example gives the title.
let title_styling = body_box(
    GroupBox::new()
        .title("Appearance")
        .variant(group_box::Variant::Outline)
        .title_color(app.tokens.foreground)
        .body(
            RadioGroup::new()
                .options(["Light", "Dark", "System"])
                .selected(app.appearance)
                .on_change(Message::Appearance),
        ),
);

#Custom Content Styling

GroupBox::new()
    .fill()
    .title("Custom Content Area")
    .content_style(
        StyleRefinement::default()
            .rounded_xl()
            .py_3()
            .px_4()
            .border_2()
            .border_color(cx.theme().accent)
    )
    .child("Content with custom styling")
Panel {
    title: "Roomy surface";
    variant: GroupBoxVariant.outline;
    content-padding: 24px;
    corner-radius: Theme.radius-xl;
    Text {
        text: "A 24px inset on a 12px radius.";
        color: Theme.foreground;
        font-size: Theme.font-size-sm;
    }
}
// A 24px inset on a 12px radius: the two knobs a caller reaches for most.
let content_styling = body_box(
    GroupBox::new()
        .title("Roomy surface")
        .variant(group_box::Variant::Outline)
        .content_padding(24.0)
        .corner_radius(theme::RADIUS_XL)
        .body(text("A 24px inset on a 12px radius.").size(theme::FONT_SIZE_SM)),
);

#Complex Example

GroupBox::new()
    .id("notification-settings")
    .outline()
    .bg(cx.theme().group_box)
    .rounded_xl()
    .p_5()
    .title("Notification Preferences")
    .title_style(
        StyleRefinement::default()
            .font_semibold()
            .line_height(relative(1.0))
            .px_3()
    )
    .content_style(
        StyleRefinement::default()
            .rounded_xl()
            .py_3()
            .px_4()
            .border_2()
    )
    .child(
        v_flex()
            .gap_3()
            .child(
                h_flex()
                    .justify_between()
                    .child("Email notifications")
                    .child(Switch::new("email").checked(true))
            )
            .child(
                h_flex()
                    .justify_between()
                    .child("Push notifications")
                    .child(Switch::new("push").checked(false))
            )
            .child(
                h_flex()
                    .justify_between()
                    .child("SMS notifications")
                    .child(Switch::new("sms").checked(false))
            )
    )
    .child(
        h_flex()
            .justify_end()
            .gap_2()
            .child(Button::new("cancel").label("Cancel"))
            .child(Button::new("save").primary().label("Save Settings"))
    )
SlintNo Slint version of this example yet.

#Form Section

GroupBox::new()
    .fill()
    .title("Personal Information")
    .child(
        v_flex()
            .gap_4()
            .child(
                h_flex()
                    .gap_2()
                    .child(Input::new("first-name").placeholder("First Name"))
                    .child(Input::new("last-name").placeholder("Last Name"))
            )
            .child(Input::new("email").placeholder("Email Address"))
            .child(
                h_flex()
                    .justify_end()
                    .child(Button::new("update").primary().label("Update Profile"))
            )
    )
SlintNo Slint version of this example yet.

#Subscription Management

GroupBox::new()
    .title("Email Subscriptions")
    .child(
        v_flex()
            .gap_2()
            .child(Checkbox::new("newsletter").label("Weekly Newsletter"))
            .child(Checkbox::new("updates").label("Product Updates"))
            .child(Checkbox::new("security").label("Security Alerts"))
            .child(Checkbox::new("marketing").label("Marketing Communications"))
    )
    .child(
        h_flex()
            .justify_between()
            .mt_4()
            .child(Button::new("unsubscribe-all").link().label("Unsubscribe All"))
            .child(Button::new("save").primary().label("Update Preferences"))
    )
SlintNo Slint version of this example yet.

#Without Title

GroupBox::new()
    .outline()
    .child(
        h_flex()
            .justify_between()
            .items_center()
            .child("Enable two-factor authentication")
            .child(Switch::new("2fa").checked(false))
    )
Panel {
    variant: GroupBoxVariant.outline;
    HorizontalLayout {
        horizontal-stretch: 1;
        spacing: 12px;
        Text {
            horizontal-stretch: 1;
            horizontal-alignment: left;
            text: "Make profile private and hide activity";
            color: Theme.foreground;
            font-size: Theme.font-size-sm;
            vertical-alignment: center;
        }
        Switch {
            checked: true;
        }
    }
}
// The title is optional: an outline surface holding one setting.
let without_title = body_box(GroupBox::new().variant(group_box::Variant::Outline).body(
    setting(
        "Make profile private and hide activity",
        Switch::new(app.private).on_toggle(Message::Private),
    ),
));

#Styling

The GroupBox component supports extensive customization through both built-in variants and custom styling:

#Theme Integration

// Using theme colors
GroupBox::new()
    .fill()
    .bg(cx.theme().group_box)
    .title("Themed Group Box")
SlintNo Slint version of this example yet.

#Custom Appearance

GroupBox::new()
    .outline()
    .border_2()
    .border_color(cx.theme().accent)
    .rounded(cx.theme().radius_lg)
    .title("Custom Styled Group Box")
    .title_style(
        StyleRefinement::default()
            .text_color(cx.theme().accent)
            .font_bold()
    )
SlintNo Slint version of this example yet.

#Best Practices

  1. Use titles for clarity - Always include a descriptive title when grouping form controls
  2. Choose appropriate variants - Use fill() for primary content groups, outline() for secondary groupings
  3. Maintain visual hierarchy - Use GroupBox to create clear sections without overwhelming the interface
  4. Group related content - Only group logically related controls and information
  5. Consider spacing - The component automatically handles internal spacing, but consider external margins
  6. Responsive design - GroupBox adapts well to different screen sizes and container widths
  • Form: Use GroupBox within forms to organize sections
  • Dialog: GroupBox works well within dialogs for organizing content
  • Accordion: For collapsible grouped content, consider using Accordion instead
  • Card: For elevated content containers with more visual weight