Skip to content
PorabuildHeroGPUI component library
v0.14.0 · Apache-2.0

Buttons

Button Group

Group related buttons with a shared variant and merged edges.

Rust
use herogpui::components::button_group::ButtonGroup;

ButtonGroup on docs.rsSource at v0.14.0

Usage

Showing Usage, live: HeroGPUI compiled to WebAssembly. The canvas has no accessibility tree; each example's description and code below carry the same information.

Usage

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{ActiveTheme, Button, ButtonGroup, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![ButtonGroup::new()
        .separators(true)
        .button(Button::new("button-group-1").label("Merge pull request"))
        .button(
            Button::new("button-group-2").is_icon_only(true).child(
                gpui::svg()
                    .size(px(16.))
                    .path(icons::CHEVRON_DOWN)
                    .text_color(cx.colors().accent.foreground),
            ),
        )
        .into_any_element()])

Merged

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, ButtonGroup, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![ButtonGroup::new()
        .variant(Variant::Secondary)
        .separators(true)
        .button(Button::new("button-group-1").label("Day"))
        .button(Button::new("button-group-2").label("Week"))
        .button(Button::new("button-group-3").label("Month"))
        .into_any_element()])

Sizes

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, ButtonGroup, Size, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(Size::ALL
        .iter()
        .filter_map(|sz| {
            let key = format!("bgroup-size-{sz:?}");
            Some({
                ButtonGroup::new()
                    .variant(Variant::Secondary)
                    .size(*sz)
                    .separators(true)
                    .button(
                        Button::new(format!("button-group-{sz:?}-1"))
                            .label("Left"),
                    )
                    .button(
                        Button::new(format!("button-group-{sz:?}-2"))
                            .label("Middle"),
                    )
                    .button(
                        Button::new(format!("button-group-{sz:?}-3"))
                            .label("Right"),
                    )
            })
        }))

With Icons

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{ActiveTheme, Button, ButtonGroup, Variant, icons};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![ButtonGroup::new()
        .variant(Variant::Tertiary)
        .separators(true)
        .button(
            Button::new("button-group-1")
                .child(gpui::svg()
                           .size(px(16.))
                           .path(icons::COPY)
                           .text_color(cx.colors().foreground)
                           .into_any_element())
                .child("Fork"),
        )
        .button(
            Button::new("button-group-2")
                .child(gpui::svg()
                           .size(px(16.))
                           .path(icons::PLUS)
                           .text_color(cx.colors().foreground)
                           .into_any_element())
                .child("Star"),
        )
        .button(
            Button::new("button-group-3")
                .is_icon_only(true)
                .child(gpui::svg()
                           .size(px(16.))
                           .path(icons::ELLIPSIS)
                           .text_color(cx.colors().foreground)
                           .into_any_element()),
        )
        .into_any_element()])

Variants

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, ButtonGroup, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(Variant::GROUP
        .iter()
        .filter_map(|v| {
            let key = format!("bgroup-v-{v:?}");
            Some({
                ButtonGroup::new()
                    .variant(*v)
                    .separators(true)
                    .button(
                        Button::new(format!("button-group-{v:?}-1")).label("One"),
                    )
                    .button(
                        Button::new(format!("button-group-{v:?}-2")).label("Two"),
                    )
            })
        }))

Orientation

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, ButtonGroup, Orientation, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![
        ButtonGroup::new()
            .variant(Variant::Secondary)
            .separators(true)
            .button(Button::new("button-group-l").label("Left"))
            .button(Button::new("button-group-c").label("Center"))
            .button(Button::new("button-group-r").label("Right"))
            .into_any_element(),
        ButtonGroup::new()
            .variant(Variant::Secondary)
            .orientation(Orientation::Vertical)
            .separators(true)
            .button(Button::new("button-group-top").label("Top"))
            .button(Button::new("button-group-mid").label("Middle"))
            .button(Button::new("button-group-bot").label("Bottom"))
            .into_any_element(),
    ])

Full Width

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, ButtonGroup, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_col()
    .items_start()
    .justify_center()
    .gap(px(12.))
    .children(vec![gpui::div()
        .w_full()
        .child(
            ButtonGroup::new()
                .variant(Variant::Secondary)
                .full_width(true)
                .separators(true)
                .button(Button::new("button-group-1").label("Cancel"))
                .button(Button::new("button-group-2").label("Save draft"))
                .button(Button::new("button-group-3").label("Publish")),
        )
        .into_any_element()])

Without Separator

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, ButtonGroup, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![ButtonGroup::new()
    .variant(Variant::Secondary)
    // v3: omit the `<ButtonGroup.Separator />` child
    // composition — the port's default draws no dividers.
    .button(Button::new("button-group-1").label("One"))
    .button(Button::new("button-group-2").label("Two"))
    .button(Button::new("button-group-3").label("Three"))
    .into_any_element()])

Disabled State

An element expression with window: &mut Window and cx: &mut App in scope.

Rust
use herogpui::prelude::{Button, ButtonGroup, Variant};
use herogpui::gpui::prelude::*;
use herogpui::gpui::px;
use herogpui::gpui;

gpui::div()
    .flex()
    .flex_wrap()
    .items_center()
    .justify_center()
    .gap(px(12.))
    .children(vec![ButtonGroup::new()
        .variant(Variant::Secondary)
        .is_disabled(true)
        .separators(true)
        .button(Button::new("button-group-1").label("One"))
        .button(Button::new("button-group-2").label("Two"))
        .into_any_element()])

Anatomy

Rust types

ButtonGroup

Button Group is assembled from these builders. The API reference lists each one.

Customization

Shared appearance belongs on ThemeBuilder::components and .recipe("name"). Typed builders change how Button Group looks at one call site. Every builder also carries sx for placement, width and flex: it takes GPUI's own styling methods and refines them over the component's root after the theme, so an instance override wins. Behaviour builders live in the API reference below.

Styling

Button Group styling
MethodValuesDescription
variant(Variant) + Button::group_defaults'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost' | 'danger' | 'danger-soft'Supplies a default variant to direct Button members while preserving explicit child variants; unset members keep Button's own default. The pinned context type is ButtonProps['variant'], so all seven values inherit.
size(Size) + Button::group_defaults'sm' | 'md' | 'lg'Supplies a default size to direct Button members while preserving explicit child sizes; unset members keep Button's own default.
orientation(Orientation)horizontal | verticalChooses row or column layout and the corresponding joined-edge geometry.
full_width(bool) + Button::group_defaultstrue | falseThe group root fills its parent, and each typed member resolves pinned button.tsx's finalFullWidth = fullWidth ?? Context.fullWidth: unset members inherit, an explicit child false/true overrides, and only resolved-full members take an equal stretch slot.
Rust
// The one slot for caller-owned low-level styling: GPUI's
// styling methods, refined over the root element last.
.sx(|el| {
    el.bg(gpui::rgba(0xffa500ff))             // background
        .text_color(gpui::rgba(0x000000ff))   // text
        .w(gpui::px(13.))                     // size
        .h(gpui::px(12.))
})

API reference

Against HeroUI v3.2.6: 12 entries ported, 4 partial, 1 not ported. Partial rows are marked in the tables; entries not ported are listed under each table with the reason.

Builders

Button Group builders
BuilderValuesDefaultDescription
id(ElementId)string—Optional GPUI identity. A named group reports role=group; an unnamed one produces no AccessKit node.
variant(Variant) + Button::group_defaults'primary' | 'secondary' | 'tertiary' | 'outline' | 'ghost' | 'danger' | 'danger-soft'—Supplies a default variant to direct Button members while preserving explicit child variants; unset members keep Button's own default. The pinned context type is ButtonProps['variant'], so all seven values inherit.
size(Size) + Button::group_defaults'sm' | 'md' | 'lg'—Supplies a default size to direct Button members while preserving explicit child sizes; unset members keep Button's own default.
orientation(Orientation)horizontal | verticalOrientation::HorizontalChooses row or column layout and the corresponding joined-edge geometry.
full_width(bool) + Button::group_defaultstrue | falsefalseThe group root fills its parent, and each typed member resolves pinned button.tsx's finalFullWidth = fullWidth ?? Context.fullWidth: unset members inherit, an explicit child false/true overrides, and only resolved-full members take an equal stretch slot.
is_disabled(bool) + Button::group_defaultstrue | falsefalseDisables direct typed members unless a child explicitly sets isDisabled=false, matching pinned context precedence.
button(Button) / child(AnyElement)PartialAnyElement—Button(Button) preserves typed direct-child inheritance and joined edges; arbitrary ParentElement children are rendered but cannot receive that context after type erasure, so they take no fullWidth stretch slot either.
separators(bool)Partialtrue | false—Synthesizes pinned separators through a composition flag rather than accepting browser classes.
Not ported1 HeroUI entry not ported
Button Group builders not ported
HeroUI v3Why it is not ported
ButtonGroup.classNameBrowser classes are unavailable.

Parts

Button Group parts
PartDescription
ButtonGroupOrientation, width, direct-child defaults, and joined-edge owner.
ButtonGroupPartialOptional hairline drawn by ButtonGroup::separators(bool) before each member after the first; composes one child per member, so per-member placement is not expressible.

States

Button Group states
StateBuilderDescription
HorizontalOrientation::HorizontalMembers form a row with start/end outer corners.
VerticalOrientation::VerticalMembers form a column with top/bottom outer corners.
Full widthfull_widthThe group fills its parent; each member that resolves fullWidth ?? Context.fullWidth shares the width equally, and an explicit child false frees its share.
DisabledButton::group_defaults precedenceUnset children inherit disabled; an explicit child false overrides it.
PressedButton group_edge skips anim::pressedGrouped buttons suppress the standalone press scale.
Focus visiblePartialButton ring_if_focused without z-10The focus ring is present, but GPUI has no z-index elevation equivalent for overlapping adjacent controls.