Button Group

A container that groups related buttons together with consistent styling.

import { createSignal } from "solid-js"

import {
  IconArchive,
  IconArrowLeft,
  IconCalendarPlus,
  IconClock,
  IconDots,
  IconFilterPlus,
  IconMailCheck,
  IconTag,
  IconTrash
} from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup } from "~/components/ui/button-group"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuPortal,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger
} from "~/components/ui/dropdown-menu"

export function ButttonGroupDemo() {
  const [label, setLabel] = createSignal("personal")
  return (
    <ButtonGroup>
      <ButtonGroup class="hidden sm:flex">
        <Button aria-label="Go Back" size="icon" variant="outline">
          <IconArrowLeft />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">Archive</Button>
        <Button variant="outline">Report</Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">Snooze</Button>
        <DropdownMenu placement="bottom-end">
          <DropdownMenuTrigger
            aria-label="More Options"
            as={Button<"button">}
            size="icon"
            variant="outline"
          >
            <IconDots />
          </DropdownMenuTrigger>
          <DropdownMenuContent class="w-52">
            <DropdownMenuGroup>
              <DropdownMenuItem>
                <IconMailCheck />
                Mark as Read
              </DropdownMenuItem>
              <DropdownMenuItem>
                <IconArchive />
                Archive
              </DropdownMenuItem>
            </DropdownMenuGroup>
            <DropdownMenuSeparator />
            <DropdownMenuGroup>
              <DropdownMenuItem>
                <IconClock />
                Snooze
              </DropdownMenuItem>
              <DropdownMenuItem>
                <IconCalendarPlus />
                Add to Calendar
              </DropdownMenuItem>
              <DropdownMenuItem>
                <IconFilterPlus />
                Add to List
              </DropdownMenuItem>
              <DropdownMenuSub overlap>
                <DropdownMenuSubTrigger>
                  <IconTag />
                  Label As...
                </DropdownMenuSubTrigger>
                <DropdownMenuPortal>
                  <DropdownMenuSubContent>
                    <DropdownMenuRadioGroup onChange={setLabel} value={label()}>
                      <DropdownMenuRadioItem value="personal">Personal</DropdownMenuRadioItem>
                      <DropdownMenuRadioItem value="work">Work</DropdownMenuRadioItem>
                      <DropdownMenuRadioItem value="other">Other</DropdownMenuRadioItem>
                    </DropdownMenuRadioGroup>
                  </DropdownMenuSubContent>
                </DropdownMenuPortal>
              </DropdownMenuSub>
            </DropdownMenuGroup>
            <DropdownMenuSeparator />
            <DropdownMenuGroup>
              <DropdownMenuItem variant="destructive">
                <IconTrash />
                Trash
              </DropdownMenuItem>
            </DropdownMenuGroup>
          </DropdownMenuContent>
        </DropdownMenu>
      </ButtonGroup>
    </ButtonGroup>
  )
}

Installation

Usage

import {
ButtonGroup,
ButtonGroupSeparator,
ButtonGroupText,
} from "@/components/ui/button-group"
<ButtonGroup>
<Button>Button 1</Button>
<Button>Button 2</Button>
</ButtonGroup>

Accessibility

  • The ButtonGroup component has the role attribute set to group.
  • Use Tab to navigate between the buttons in the group.
  • Use aria-label or aria-labelledby to label the button group.
<ButtonGroup aria-label="Button group">
<Button>Button 1</Button>
<Button>Button 2</Button>
</ButtonGroup>

ButtonGroup vs ToggleGroup

  • Use the ButtonGroup component when you want to group buttons that perform an action.
  • Use the ToggleGroup component when you want to group buttons that toggle a state.

Examples

Orientation

Set the orientation prop to change the button group layout.

import { IconMinus, IconPlus } from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup } from "~/components/ui/button-group"

export function ButtonGroupOrientation() {
  return (
    <ButtonGroup aria-label="Media controls" class="h-fit" orientation="vertical">
      <Button size="icon" variant="outline">
        <IconPlus />
      </Button>
      <Button size="icon" variant="outline">
        <IconMinus />
      </Button>
    </ButtonGroup>
  )
}

Size

Control the size of buttons using the size prop on individual buttons.

import { IconPlus } from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup } from "~/components/ui/button-group"

export function ButtonGroupSize() {
  return (
    <div class="flex flex-col items-start gap-8">
      <ButtonGroup>
        <Button size="sm" variant="outline">
          Small
        </Button>
        <Button size="sm" variant="outline">
          Button
        </Button>
        <Button size="sm" variant="outline">
          Group
        </Button>
        <Button size="icon-sm" variant="outline">
          <IconPlus />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">Default</Button>
        <Button variant="outline">Button</Button>
        <Button variant="outline">Group</Button>
        <Button size="icon" variant="outline">
          <IconPlus />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button size="lg" variant="outline">
          Large
        </Button>
        <Button size="lg" variant="outline">
          Button
        </Button>
        <Button size="lg" variant="outline">
          Group
        </Button>
        <Button size="icon-lg" variant="outline">
          <IconPlus />
        </Button>
      </ButtonGroup>
    </div>
  )
}

Nested

Nest <ButtonGroup> components to create button groups with spacing.

import { IconArrowLeft, IconArrowRight } from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup } from "~/components/ui/button-group"

export function ButtonGroupNested() {
  return (
    <ButtonGroup>
      <ButtonGroup>
        <Button size="sm" variant="outline">
          1
        </Button>
        <Button size="sm" variant="outline">
          2
        </Button>
        <Button size="sm" variant="outline">
          3
        </Button>
        <Button size="sm" variant="outline">
          4
        </Button>
        <Button size="sm" variant="outline">
          5
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button aria-label="Previous" size="icon-sm" variant="outline">
          <IconArrowLeft />
        </Button>
        <Button aria-label="Next" size="icon-sm" variant="outline">
          <IconArrowRight />
        </Button>
      </ButtonGroup>
    </ButtonGroup>
  )
}

Separator

The ButtonGroupSeparator component visually divides buttons within a group.

Buttons with variant outline do not need a separator since they have a border. For other variants, a separator is recommended to improve the visual hierarchy.


import { Button } from "~/components/ui/button"
import { ButtonGroup, ButtonGroupSeparator } from "~/components/ui/button-group"

export function ButtonGroupSeparatorDemo() {
  return (
    <ButtonGroup>
      <Button size="sm" variant="secondary">
        Copy
      </Button>
      <ButtonGroupSeparator />
      <Button size="sm" variant="secondary">
        Paste
      </Button>
    </ButtonGroup>
  )
}

Split

Create a split button group by adding two buttons separated by a ButtonGroupSeparator.


import { IconPlus } from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup, ButtonGroupSeparator } from "~/components/ui/button-group"

export function ButtonGroupSplit() {
  return (
    <ButtonGroup>
      <Button variant="secondary">Button</Button>
      <ButtonGroupSeparator />
      <Button size="icon" variant="secondary">
        <IconPlus />
      </Button>
    </ButtonGroup>
  )
}

Input

Wrap an Input component with buttons.

import { IconSearch } from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup } from "~/components/ui/button-group"
import { Input } from "~/components/ui/input"

export function ButtonGroupInput() {
  return (
    <ButtonGroup>
      <Input placeholder="Search..." />
      <Button aria-label="Search" variant="outline">
        <IconSearch />
      </Button>
    </ButtonGroup>
  )
}

Input Group

Wrap an InputGroup component to create complex input layouts.

import { createSignal } from "solid-js"

import { IconAudioLines, IconPlus } from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup } from "~/components/ui/button-group"
import {
  InputGroup,
  InputGroupAddon,
  InputGroupButton,
  InputGroupInput
} from "~/components/ui/input-group"
import { Tooltip, TooltipContent, TooltipTrigger } from "~/components/ui/tooltip"

export function ButtonGroupInputGroup() {
  const [voiceEnabled, setVoiceEnabled] = createSignal(false)

  return (
    <ButtonGroup class="[--radius:9999rem]">
      <ButtonGroup>
        <Button size="icon" variant="outline">
          <IconPlus />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <InputGroup>
          <InputGroupInput
            disabled={voiceEnabled()}
            placeholder={voiceEnabled() ? "Record and send audio..." : "Send a message..."}
          />
          <InputGroupAddon align="inline-end">
            <Tooltip>
              <InputGroupButton
                aria-pressed={voiceEnabled}
                as={TooltipTrigger}
                class="data-[active=true]:bg-orange-100 data-[active=true]:text-orange-700 dark:data-[active=true]:bg-orange-800 dark:data-[active=true]:text-orange-100"
                data-active={voiceEnabled}
                onClick={() => setVoiceEnabled(!voiceEnabled)}
                size="icon-xs"
              >
                <IconAudioLines />
              </InputGroupButton>

              <TooltipContent>Voice Mode</TooltipContent>
            </Tooltip>
          </InputGroupAddon>
        </InputGroup>
      </ButtonGroup>
    </ButtonGroup>
  )
}

Create a split button group with a DropdownMenu component.

import {
  IconAlertTriangle,
  IconCheck,
  IconChevronDown,
  IconCopy,
  IconShare,
  IconTrash,
  IconUserRoundX,
  IconVolumeOff
} from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup } from "~/components/ui/button-group"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger
} from "~/components/ui/dropdown-menu"

export function ButtonGroupDropdown() {
  return (
    <ButtonGroup>
      <Button variant="outline">Follow</Button>
      <DropdownMenu placement="bottom-end">
        <Button as={DropdownMenuTrigger} class="!pl-2" variant="outline">
          <IconChevronDown />
        </Button>
        <DropdownMenuContent class="[--radius:1rem]">
          <DropdownMenuGroup>
            <DropdownMenuItem>
              <IconVolumeOff />
              Mute Conversation
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconCheck />
              Mark as Read
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconAlertTriangle />
              Report Conversation
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconUserRoundX />
              Block User
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconShare />
              Share Conversation
            </DropdownMenuItem>
            <DropdownMenuItem>
              <IconCopy />
              Copy Conversation
            </DropdownMenuItem>
          </DropdownMenuGroup>
          <DropdownMenuSeparator />
          <DropdownMenuGroup>
            <DropdownMenuItem variant="destructive">
              <IconTrash />
              Delete Conversation
            </DropdownMenuItem>
          </DropdownMenuGroup>
        </DropdownMenuContent>
      </DropdownMenu>
    </ButtonGroup>
  )
}

Select

Pair with a Select component.

import { createSignal } from "solid-js"

import { IconArrowRight } from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup } from "~/components/ui/button-group"
import { Input } from "~/components/ui/input"
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue
} from "~/components/ui/select"

type Currency = {
  value: string
  label: string
}

const CURRENCIES: Currency[] = [
  {
    value: "$",
    label: "US Dollar"
  },
  {
    value: "€",
    label: "Euro"
  },
  {
    value: "£",
    label: "British Pound"
  }
]

export function ButtonGroupSelect() {
  const [currency, setCurrency] = createSignal<Currency>(CURRENCIES[0])

  return (
    <ButtonGroup>
      <ButtonGroup>
        <Select
          itemComponent={(props) => (
            <SelectItem item={props.item}>
              {props.item.rawValue.value}{" "}
              <span class="text-muted-foreground">{props.item.rawValue.label}</span>
            </SelectItem>
          )}
          onChange={setCurrency}
          options={CURRENCIES}
          optionTextValue="label"
          optionValue="value"
          value={currency()}
        >
          <SelectTrigger class="font-mono">
            <SelectValue<Currency>>{(state) => state.selectedOption().value}</SelectValue>
          </SelectTrigger>
          <SelectContent class="min-w-24" />
        </Select>
        <Input pattern="[0-9]*" placeholder="10.00" />
      </ButtonGroup>
      <ButtonGroup>
        <Button aria-label="Send" size="icon" variant="outline">
          <IconArrowRight />
        </Button>
      </ButtonGroup>
    </ButtonGroup>
  )
}

Popover

Use with a Popover component.

import { IconBot, IconChevronDown } from "~/components/icons"
import { Button } from "~/components/ui/button"
import { ButtonGroup } from "~/components/ui/button-group"
import { Popover, PopoverContent, PopoverTrigger } from "~/components/ui/popover"
import { Separator } from "~/components/ui/separator"
import { Textarea } from "~/components/ui/textarea"

export function ButtonGroupPopover() {
  return (
    <ButtonGroup>
      <Button variant="outline">
        <IconBot /> Copilot
      </Button>
      <Popover placement="bottom-end">
        <Button aria-label="Open Popover" as={PopoverTrigger} size="icon" variant="outline">
          <IconChevronDown />
        </Button>

        <PopoverContent class="rounded-xl p-0 text-sm">
          <div class="px-4 py-3">
            <div class="font-medium text-sm">Agent Tasks</div>
          </div>
          <Separator />
          <div class="p-4 text-sm *:[p:not(:last-child)]:mb-2">
            <Textarea
              class="mb-4 resize-none"
              placeholder="Describe your task in natural language."
            />
            <p class="font-medium">Start a new task with Copilot</p>
            <p class="text-muted-foreground">
              Describe your task in natural language. Copilot will work in the background and open a
              pull request for your review.
            </p>
          </div>
        </PopoverContent>
      </Popover>
    </ButtonGroup>
  )
}

API Reference

ButtonGroup

The ButtonGroup component is a container that groups related buttons together with consistent styling.

PropTypeDefault
orientation"horizontal" | "vertical""horizontal"
<ButtonGroup>
<Button>Button 1</Button>
<Button>Button 2</Button>
</ButtonGroup>

Nest multiple button groups to create complex layouts with spacing. See the nested example for more details.

<ButtonGroup>
<ButtonGroup />
<ButtonGroup />
</ButtonGroup>

ButtonGroupSeparator

The ButtonGroupSeparator component visually divides buttons within a group.

PropTypeDefault
orientation"horizontal" | "vertical""vertical"
<ButtonGroup>
<Button>Button 1</Button>
<ButtonGroupSeparator />
<Button>Button 2</Button>
</ButtonGroup>

ButtonGroupText

Use this component to display text within a button group.

<ButtonGroup>
<ButtonGroupText>Text</ButtonGroupText>
<Button>Button</Button>
</ButtonGroup>

Use the as prop to render a custom component as the text, for example a label.

import { ButtonGroupText } from "~/components/ui/button-group"
import { Label } from "~/components/ui/label"
export function ButtonGroupTextDemo() {
return (
<ButtonGroup>
<Label as={ButtonGroupText} htmlFor="name">Text</Label>
<Input placeholder="Type something here..." id="name" />
</ButtonGroup>
)
}