Biji UI

Radio Group

A set of checkable buttons where only one can be selected at a time.

Installation

biji-ui = { version = "0.5.0", features = ["radio_group"] }

Usage

use leptos::prelude::*;
use biji_ui::components::radio_group;

#[component]
pub fn MyRadioGroup() -> impl IntoView {
    view! {
        <radio_group::Root class="flex flex-col gap-2">
            <label class="flex items-center gap-2 cursor-pointer">
                <radio_group::Item
                    value="option-a"
                    class="flex items-center justify-center w-5 h-5 rounded-full border-2 border-border data-[state=checked]:border-primary"
                >
                    <radio_group::Indicator class="w-2.5 h-2.5 rounded-full bg-primary hidden data-[state=checked]:block" />
                </radio_group::Item>
                <span class="text-sm">"Option A"</span>
            </label>
            <label class="flex items-center gap-2 cursor-pointer">
                <radio_group::Item
                    value="option-b"
                    class="flex items-center justify-center w-5 h-5 rounded-full border-2 border-border data-[state=checked]:border-primary"
                >
                    <radio_group::Indicator class="w-2.5 h-2.5 rounded-full bg-primary hidden data-[state=checked]:block" />
                </radio_group::Item>
                <span class="text-sm">"Option B"</span>
            </label>
        </radio_group::Root>
    }
}

RootWith

Use <RootWith> when you need direct access to RadioGroupState inside the children. The let:rg binding exposes rg.value as a reactive signal for reading the current selection without callbacks.

Selected: pro

use leptos::prelude::*;
use biji_ui::components::radio_group;

#[component]
pub fn PlanSelector() -> impl IntoView {
    let item_class = "flex items-center justify-center w-5 h-5 rounded-full border-2 \
        border-border data-[state=checked]:border-primary";
    let indicator_class = "w-2.5 h-2.5 rounded-full bg-primary hidden data-[state=checked]:block";

    view! {
        <radio_group::RootWith default_value="pro".to_string() class="flex flex-col gap-3" let:rg>
            {["Free", "Pro", "Enterprise"].into_iter().map(|label| {
                let value = label.to_lowercase();
                view! {
                    <label class="flex items-center gap-3 cursor-pointer select-none">
                        <radio_group::Item value={value} class={item_class}>
                            <radio_group::Indicator class={indicator_class} />
                        </radio_group::Item>
                        <span class="text-sm font-medium">{label}</span>
                    </label>
                }
            }).collect_view()}
            <p class="text-xs text-muted-foreground mt-1">
                "Selected: " {move || rg.value.get().unwrap_or_default()}
            </p>
        </radio_group::RootWith>
    }
}

API Reference

Root / RootWith

NameTypeDefaultDescription
classString""CSS class applied to the radio group container.
valueOption<String>NoneThe initial selected value of the radio group.
disabledboolfalseWhen true, disables all radio items in the group.

Item

NameTypeDefaultDescription
valueStringrequiredThe value of this radio item. Used to identify the selected option.
classString""CSS class applied to the radio item button.
disabledboolfalseWhen true, prevents this radio item from being selected.

Indicator

NameTypeDefaultDescription
classString""CSS class applied to the indicator span.

Data Attributes

AttributeDescription
data-state"checked" when this item is selected; "unchecked" otherwise. Present on Item and Indicator.
data-disabledPresent on Root when the group is disabled. Present on Item and Indicator when the item is disabled.

Keyboard Navigation

KeyDescription
ArrowDown / ArrowRightMoves focus and selection to the next radio item, wrapping around.
ArrowUp / ArrowLeftMoves focus and selection to the previous radio item, wrapping around.