Button Group

A component that groups multiple buttons together with shared borders and styling.

button
use leptos::prelude::*;

use crate::components::ui::button::{Button, ButtonVariant};
use crate::components::ui::button_group::ButtonGroup;

#[component]
pub fn DemoButtonGroup() -> impl IntoView {
    view! {
        <ButtonGroup>
            <Button variant=ButtonVariant::Outline>"First"</Button>
            <Button variant=ButtonVariant::Outline>"Second"</Button>
            <Button variant=ButtonVariant::Outline>"Third"</Button>
        </ButtonGroup>
    }
}

Installation

You can run either of the following commands:

# cargo install ui-cli --force
ui add demo_button_group
ui add button_group

Update the imports to match your project setup.

Copy and paste the following code into your project:

components/ui/button_group.rs

use leptos::prelude::*;
use leptos_ui::clx;
use tw_merge::*;

use super::separator::{Separator, SeparatorOrientation};

mod components {
    use super::*;
    clx! {ButtonGroupText, span, "bg-muted flex items-center gap-2 rounded-md border px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4"}
}

pub use components::*;

/* ========================================================== */
/*                     ✨ FUNCTIONS ✨                        */
/* ========================================================== */

#[component]
pub fn ButtonGroup(
    #[prop(into, optional)] orientation: Signal<ButtonGroupOrientation>,
    #[prop(into, optional)] class: String,
    children: Children,
) -> impl IntoView {
    let merged_class = Memo::new(move |_| {
        let orientation = orientation.get();
        let button_group = ButtonGroupClass { orientation };
        button_group.with_class(class.clone())
    });

    view! {
        <div data-name="ButtonGroup" role="group" class=merged_class>
            {children()}
        </div>
    }
}

/* ========================================================== */
/*                     ✨ FUNCTIONS ✨                        */
/* ========================================================== */

#[derive(TwClass, Default)]
#[tw(
    class = "flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative [&>[data-slot=select-trigger]:not([class*='w-'])]:w-fit [&>input]:flex-1 has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-md has-[>[data-slot=button-group]]:gap-2"
)]
pub struct ButtonGroupClass {
    pub orientation: ButtonGroupOrientation,
}

#[derive(TwVariant)]
pub enum ButtonGroupOrientation {
    #[tw(
        default,
        class = "[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>*:not(:last-child)]:rounded-r-none"
    )]
    Horizontal,
    #[tw(
        class = "flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none"
    )]
    Vertical,
}

#[component]
pub fn ButtonGroupSeparator(
    #[prop(into, optional, default = SeparatorOrientation::Vertical.into())] orientation: Signal<SeparatorOrientation>,
    #[prop(into, optional)] class: String,
) -> impl IntoView {
    let merged_class = tw_merge!("relative !m-0 self-stretch data-[orientation=vertical]:h-auto", class);

    view! { <Separator attr:data-name="ButtonGroupSeparator" orientation=orientation class=merged_class /> }
}

Update the imports to match your project setup.

Usage

You can use the ButtonGroup component in combination with the Button component.

use crate::components::ui::button::{Button, ButtonVariant};
use crate::components::ui::button_group::ButtonGroup;
<ButtonGroup>
    <Button variant=ButtonVariant::Outline>"First"</Button>
    <Button variant=ButtonVariant::Outline>"Second"</Button>
    <Button variant=ButtonVariant::Outline>"Third"</Button>
</ButtonGroup>

Examples

With Separator

Button Group with visual separators between adjacent Button components for improved visual clarity. This example demonstrates how to use ButtonGroupSeparator in Leptos to create segmented button controls with clear boundaries and enhanced usability in Rust UI components.

use leptos::prelude::*;

use crate::components::ui::button::{Button, ButtonVariant};
use crate::components::ui::button_group::{ButtonGroup, ButtonGroupSeparator};

#[component]
pub fn DemoButtonGroupSeparator() -> impl IntoView {
    view! {
        <ButtonGroup>
            <Button variant=ButtonVariant::Secondary>"Copy"</Button>
            <ButtonGroupSeparator />
            <Button variant=ButtonVariant::Secondary>"Paste"</Button>
            <ButtonGroupSeparator />
            <Button variant=ButtonVariant::Secondary>"Cut"</Button>
        </ButtonGroup>
    }
}

With Icons

Button Group component supporting icon Button components with both horizontal and vertical orientations. This example shows how to combine Lucide icons with button groups in Leptos for creating compact toolbar interfaces and icon-based navigation in Rust applications.

use icons::{Minus, Plus};
use leptos::prelude::*;

use crate::components::ui::button::{Button, ButtonSize, ButtonVariant};
use crate::components::ui::button_group::{ButtonGroup, ButtonGroupOrientation};

#[component]
pub fn DemoButtonGroupIcon() -> impl IntoView {
    view! {
        <ButtonGroup orientation=ButtonGroupOrientation::Vertical>
            <Button variant=ButtonVariant::Outline size=ButtonSize::Icon>
                <Plus />
            </Button>
            <Button variant=ButtonVariant::Outline size=ButtonSize::Icon>
                <Minus />
            </Button>
        </ButtonGroup>
    }
}

Get notified when new stuff drops.

Rust/UI Icons - Send