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.
#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),
),
);#Footer outside the surface
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
- Use titles for clarity - Always include a descriptive title when grouping form controls
- Choose appropriate variants - Use
fill()for primary content groups,outline()for secondary groupings - Maintain visual hierarchy - Use GroupBox to create clear sections without overwhelming the interface
- Group related content - Only group logically related controls and information
- Consider spacing - The component automatically handles internal spacing, but consider external margins
- Responsive design - GroupBox adapts well to different screen sizes and container widths
#Related Components
- 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