Back to Primitives

ButtonGroup

enhancedActions
View on GitHub

Groups related actions with automatic slotting.

What we add: Auto-slotting (last → primary, others → secondary)

Example
CancelSave
Usage
import { ButtonGroup, Button } from '@/components/polaris';

function Example() {
    return (
        <ButtonGroup>
            <Button variant="secondary">Cancel</Button>
            <Button variant="primary">Save</Button>
        </ButtonGroup>
    );
}
Component Source
import { forwardRef, createElement, Children, isValidElement, cloneElement, type ReactNode } from 'react';

type SButtonGroupProps = JSX.IntrinsicElements['s-button-group'];

export type ButtonGroupProps = Omit<SButtonGroupProps, 'children'> & {
    children?: ReactNode;
};

/**
 * ButtonGroup component - groups related actions.
 *
 * Automatically assigns slots based on position:
 * - Last child → `primary-action` slot
 * - Other children → `secondary-actions` slot
 *
 *
 * @example
 * <ButtonGroup>
 *   <Button>Cancel</Button>
 *   <Button variant="primary">Save</Button>
 * </ButtonGroup>
 */
export const ButtonGroup = forwardRef<HTMLElement, ButtonGroupProps>(({ children, ...props }, ref) => {
    const buttons = Children.toArray(children);

    const slottedChildren = buttons.map((child, index) => {
        if (!isValidElement<{ slot?: string }>(child)) return child;

        // Respect explicit slot if provided
        if (child.props.slot) return child;

        const isLast = index === buttons.length - 1;
        const slot = isLast ? 'primary-action' : 'secondary-actions';

        return cloneElement(child, { slot });
    });

    return createElement('s-button-group', { ref, ...props }, slottedChildren);
});

ButtonGroup.displayName = 'ButtonGroup';