Group

Groups controls with unified borders.

import { Group } from 'ui-lab-components/group';
SpacingNone
import { Fragment } from 'react';
import { Divider } from 'ui-lab-components/divider';
import { Group } from 'ui-lab-components/group';
 
const actions = ['Save', 'Duplicate', 'Archive'];
 
export default function Example() {
  return (
    <Group>
      {actions.map((action, index) => (
        <Fragment key={action}>
          {index > 0 && <Divider />}
          <Group.Button>{action}</Group.Button>
        </Fragment>
      ))}
    </Group>
  );
}
 

group/01-basic-group

A simple group container that arranges multiple elements together. Use this to organize related UI elements in a consistent layout.

Loading interactive preview
import { Fragment } from 'react';
import { Group } from 'ui-lab-components/group';
import { Divider } from 'ui-lab-components/divider';
import { Icon } from 'ui-lab-components/icon';
 
const actions = [
  { label: 'Edit', icon: 'compose' },
  { label: 'Copy', icon: 'copy' },
  { label: 'Delete', icon: 'trash' },
];
 
export default function Example() {
  return (
    <Group>
      {actions.map(({ label, icon }, index) => (
        <Fragment key={label}>
          {index > 0 && <Divider />}
          <Group.Button icon={{ left: <Icon id={icon} size="1em" /> }}>{label}</Group.Button>
        </Fragment>
      ))}
    </Group>
  );
}
 

group/02-compact-group

An icon-only group of actions with a tooltip hint on each button, shown beside the basic group.

Loading interactive preview
import { Fragment } from 'react';
import { Flex } from 'ui-lab-components/flex';
import { Group } from 'ui-lab-components/group';
import { Divider } from 'ui-lab-components/divider';
import { Tooltip } from 'ui-lab-components/tooltip';
import { Icon } from 'ui-lab-components/icon';
 
const actions = [
  { label: 'Edit', hint: 'Edit this item', key: 'E', icon: 'compose' },
  { label: 'Copy', hint: 'Copy to clipboard', key: 'C', icon: 'copy' },
  { label: 'Delete', hint: 'Delete this item', key: 'D', icon: 'trash' },
];
 
export default function Example() {
  return (
    <Flex gap="md" align-center>
      <Group aria-label="Item actions">
        {actions.map(({ label, hint, key, icon }, index) => (
          <Fragment key={label}>
            {index > 0 && <Divider />}
            <Tooltip content={hint} hint={key} position="top">
              <Group.Button aria-label={label} icon={<Icon id={icon} size="1em" />} />
            </Tooltip>
          </Fragment>
        ))}
      </Group>
      <Group>
        {actions.map(({ label, icon }, index) => (
          <Fragment key={label}>
            {index > 0 && <Divider />}
            <Group.Button icon={{ left: <Icon id={icon} size="1em" /> }}>{label}</Group.Button>
          </Fragment>
        ))}
      </Group>
    </Flex>
  );
}
 

group/03-filter-bar

Horizontal group combining Select dropdowns with action buttons for filtering interfaces.

Loading interactive preview
"use client";
 
import { useState } from "react";
import { Divider } from 'ui-lab-components/divider'
import { Group } from 'ui-lab-components/group'
import { Select } from 'ui-lab-components/select';
import { Icon } from "ui-lab-components/icon"
 
export default function Example() {
  const [status, setStatus] = useState<string | number | null>("active");
 
  return (
    <Group variant="default" orientation="horizontal">
      <Group.Input icon={<Icon id="search" size="1em" />} placeholder="Search...">
      </Group.Input>
      <Divider />
      <Group.Select selectedKey={status} onSelectionChange={setStatus} className="w-36">
        <Select.Trigger><Select.Value placeholder="Status" /></Select.Trigger>
        <Select.Content>
          <Select.List>
            <Select.Item value="active" textValue="Active">Active</Select.Item>
            <Select.Item value="inactive" textValue="Inactive">Inactive</Select.Item>
            <Select.Item value="pending" textValue="Pending">Pending</Select.Item>
          </Select.List>
        </Select.Content>
      </Group.Select>
      <Divider />
      <Group.Button size="md"><Icon id="filter" size="1em" className="mr-1.5" /> Apply</Group.Button>
    </Group>
  );
}
 

group/06-copy-command

A read-only command field with a joined copy action.

Loading interactive preview
import { Divider } from 'ui-lab-components/divider'
import { Group } from 'ui-lab-components/group';
import { Icon } from "ui-lab-components/icon"
 
export default function Example() {
  return (
    <Group>
      <Group.Input defaultValue="npm install ui-lab" readOnly className="w-55 font-mono text-sm" />
      <Divider />
      <Group.Button variant="secondary" icon={{ left: <Icon id="copy" size="1em" className="text-foreground-400" /> }} />
    </Group>
  );
}
 

group/08-slider-integration

Numeric input synced with a slider for precise value selection.

Loading interactive preview
"use client";
 
import { useState } from "react";
import { Group } from 'ui-lab-components/group'
import { Slider } from 'ui-lab-components/slider';
import { Icon } from "ui-lab-components/icon";
 
export default function Example() {
  const [sliderValue, setSliderValue] = useState<number[]>([45]);
  const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const val = parseInt(e.target.value);
    if (!isNaN(val)) setSliderValue([Math.min(Math.max(val, 0), 100)]);
  };
 
  return (
    <div className="space-y-4 w-64">
      <Group>
        <Group.Input type="number" min={0} max={100} value={sliderValue[0]} onChange={handleInputChange} className="w-full" />
        <div className="bg-background-800 flex items-center px-3 text-foreground-400 text-sm font-medium">
          <Icon id="percent" size="1em" />
        </div>
      </Group>
      <Slider.Root value={sliderValue} onValueChange={setSliderValue} max={100} step={1} />
    </div>
  );
}