Command Palette

Search for a command...

Button Group

Button Group represents a set of related actions.

"use client";

import * as React from "react";
import {
  LuArchive,
  LuArrowLeft,
  LuCalendarPlus,
  LuClock,
  LuEllipsis,
  LuFilter,
  LuMailCheck,
  LuTag,
  LuTrash2,
} from "react-icons/lu";
import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";

export default function ButtonGroupDemo() {
  const [label, setLabel] = React.useState("personal");

  return (
    <ButtonGroup>
      <ButtonGroup css={{ display: "none", sm: { display: "flex" } }}>
        <Button variant="outline" size="icon" aria-label="Go Back">
          <LuArrowLeft />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">
          <LuArchive /> Archive
        </Button>
        <Button variant="outline">
          <LuEllipsis /> Report
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">
          <LuClock /> Snooze
        </Button>
        <DropdownMenu>
          <DropdownMenuTrigger asChild>
            <Button variant="outline" size="icon" aria-label="More Options">
              <LuEllipsis />
            </Button>
          </DropdownMenuTrigger>
          <DropdownMenuContent align="end" css={{ w: "52" }}>
            <DropdownMenuGroup>
              <DropdownMenuItem>
                <LuMailCheck />
                Mark as Read
              </DropdownMenuItem>
              <DropdownMenuItem>
                <LuArchive />
                Archive
              </DropdownMenuItem>
            </DropdownMenuGroup>
            <DropdownMenuSeparator />
            <DropdownMenuGroup>
              <DropdownMenuItem>
                <LuClock />
                Snooze
              </DropdownMenuItem>
              <DropdownMenuItem>
                <LuCalendarPlus />
                Add to Calendar
              </DropdownMenuItem>
              <DropdownMenuItem>
                <LuFilter />
                Add to List
              </DropdownMenuItem>
              <DropdownMenuSub>
                <DropdownMenuSubTrigger>
                  <LuTag />
                  Label As...
                </DropdownMenuSubTrigger>
                <DropdownMenuSubContent>
                  <DropdownMenuRadioGroup value={label} onValueChange={setLabel}>
                    <DropdownMenuRadioItem value="personal">Personal</DropdownMenuRadioItem>
                    <DropdownMenuRadioItem value="work">Work</DropdownMenuRadioItem>
                    <DropdownMenuRadioItem value="other">Other</DropdownMenuRadioItem>
                  </DropdownMenuRadioGroup>
                </DropdownMenuSubContent>
              </DropdownMenuSub>
            </DropdownMenuGroup>
            <DropdownMenuSeparator />
            <DropdownMenuGroup>
              <DropdownMenuItem variant="danger">
                <LuTrash2 />
                Trash
              </DropdownMenuItem>
            </DropdownMenuGroup>
          </DropdownMenuContent>
        </DropdownMenu>
      </ButtonGroup>
    </ButtonGroup>
  );
}

Installation

npx nore-ui-cli@latest add button-group

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 { LuMinus, LuPlus } from "react-icons/lu";
import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";

export default function ButtonGroupOrientation() {
  return (
    <ButtonGroup orientation="vertical" aria-label="Media controls" css={{ h: "fit" }}>
      <Button variant="outline" size="icon">
        <LuPlus />
      </Button>
      <Button variant="outline" size="icon">
        <LuMinus />
      </Button>
    </ButtonGroup>
  );
}

Size

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

import { LuPlus } from "react-icons/lu";
import { styled } from "styled-system/jsx";
import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";

export default function ButtonGroupSize() {
  return (
    <styled.div css={{ display: "flex", flexDir: "column", alignItems: "flex-start", gap: "8" }}>
      <ButtonGroup>
        <Button variant="outline" size="sm">
          Small
        </Button>
        <Button variant="outline" size="sm">
          Button
        </Button>
        <Button variant="outline" size="sm">
          Group
        </Button>
        <Button variant="outline" size="icon-sm">
          <LuPlus />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline">Default</Button>
        <Button variant="outline">Button</Button>
        <Button variant="outline">Group</Button>
        <Button variant="outline" size="icon">
          <LuPlus />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline" size="lg">
          Large
        </Button>
        <Button variant="outline" size="lg">
          Button
        </Button>
        <Button variant="outline" size="lg">
          Group
        </Button>
        <Button variant="outline" size="icon-lg">
          <LuPlus />
        </Button>
      </ButtonGroup>
    </styled.div>
  );
}

Nested

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

"use client";

import { LuArrowLeft, LuArrowRight } from "react-icons/lu";
import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";

export default function ButtonGroupNested() {
  return (
    <ButtonGroup>
      <ButtonGroup>
        <Button variant="outline" size="sm">
          1
        </Button>
        <Button variant="outline" size="sm">
          2
        </Button>
        <Button variant="outline" size="sm">
          3
        </Button>
        <Button variant="outline" size="sm">
          4
        </Button>
        <Button variant="outline" size="sm">
          5
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <Button variant="outline" size="icon-sm" aria-label="Previous">
          <LuArrowLeft />
        </Button>
        <Button variant="outline" size="icon-sm" aria-label="Next">
          <LuArrowRight />
        </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 default function ButtonGroupSeparatorDemo() {
  return (
    <ButtonGroup>
      <Button variant="secondary" size="sm">
        Copy
      </Button>
      <ButtonGroupSeparator />
      <Button variant="secondary" size="sm">
        Paste
      </Button>
    </ButtonGroup>
  );
}

Split

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

import { TbPlus } from "react-icons/tb";
import { Button } from "@/components/ui/button";
import { ButtonGroup, ButtonGroupSeparator } from "@/components/ui/button-group";

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

Input

Wrap an Input component with buttons.

import { LuSearch } from "react-icons/lu";
import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";
import { Input } from "@/components/ui/input";

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

Input Group

Wrap an InputGroup component to create complex input layouts.

"use client";

import * as React from "react";
import { LuAudioLines, LuPlus } from "react-icons/lu";
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 default function ButtonGroupInputGroup() {
  const [voiceEnabled, setVoiceEnabled] = React.useState(false);

  return (
    <ButtonGroup>
      <ButtonGroup>
        <Button variant="outline" size="icon" css={{ rounded: "full" }}>
          <LuPlus />
        </Button>
      </ButtonGroup>
      <ButtonGroup>
        <InputGroup css={{ rounded: "full" }}>
          <InputGroupInput
            placeholder={voiceEnabled ? "Record and send audio..." : "Send a message..."}
            disabled={voiceEnabled}
          />
          <InputGroupAddon align="inline-end">
            <Tooltip>
              <TooltipTrigger asChild>
                <InputGroupButton
                  onClick={() => setVoiceEnabled(!voiceEnabled)}
                  size="icon-xs"
                  data-active={voiceEnabled}
                  css={{
                    rounded: "full",
                    "&[data-active=true]": {
                      bg: "orange.100",
                      color: "orange.700",
                      _dark: {
                        bg: "orange.800",
                        color: "orange.100",
                      },
                    },
                  }}
                  aria-pressed={voiceEnabled}
                >
                  <LuAudioLines />
                </InputGroupButton>
              </TooltipTrigger>
              <TooltipContent>Voice Mode</TooltipContent>
            </Tooltip>
          </InputGroupAddon>
        </InputGroup>
      </ButtonGroup>
    </ButtonGroup>
  );
}

Create a split button group with a DropdownMenu component.

"use client";

import {
  LuCheck,
  LuChevronDown,
  LuCopy,
  LuShare,
  LuTrash,
  LuTriangleAlert,
  LuUserRoundX,
  LuVolumeOff,
} from "react-icons/lu";
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 default function ButtonGroupDropdown() {
  return (
    <ButtonGroup>
      <Button variant="outline">Follow</Button>
      <DropdownMenu>
        <DropdownMenuTrigger asChild>
          <Button variant="outline" css={{ pl: "2!" }}>
            <LuChevronDown />
          </Button>
        </DropdownMenuTrigger>
        <DropdownMenuContent align="end">
          <DropdownMenuGroup>
            <DropdownMenuItem>
              <LuVolumeOff />
              Mute Conversation
            </DropdownMenuItem>
            <DropdownMenuItem>
              <LuCheck />
              Mark as Read
            </DropdownMenuItem>
            <DropdownMenuItem>
              <LuTriangleAlert />
              Report Conversation
            </DropdownMenuItem>
            <DropdownMenuItem>
              <LuUserRoundX />
              Block User
            </DropdownMenuItem>
            <DropdownMenuItem>
              <LuShare />
              Share Conversation
            </DropdownMenuItem>
            <DropdownMenuItem>
              <LuCopy />
              Copy Conversation
            </DropdownMenuItem>
          </DropdownMenuGroup>
          <DropdownMenuSeparator />
          <DropdownMenuGroup>
            <DropdownMenuItem variant="danger">
              <LuTrash />
              Delete Conversation
            </DropdownMenuItem>
          </DropdownMenuGroup>
        </DropdownMenuContent>
      </DropdownMenu>
    </ButtonGroup>
  );
}

Select

Pair with a Select component.

"use client";

import * as React from "react";
import { LuArrowRight } from "react-icons/lu";
import { styled } from "styled-system/jsx";
import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select";

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

export default function ButtonGroupSelect() {
  const [currency, setCurrency] = React.useState("$");

  return (
    <ButtonGroup>
      <ButtonGroup>
        <Select value={currency} onValueChange={setCurrency}>
          <SelectTrigger css={{ fontFamily: "mono" }}>{currency}</SelectTrigger>
          <SelectContent css={{ minW: "24" }}>
            {CURRENCIES.map((currency) => (
              <SelectItem key={currency.value} value={currency.value}>
                {currency.value}{" "}
                <styled.span css={{ color: "muted.fg" }}>{currency.label}</styled.span>
              </SelectItem>
            ))}
          </SelectContent>
        </Select>
        <Input placeholder="10.00" pattern="[0-9]*" />
      </ButtonGroup>
      <ButtonGroup>
        <Button aria-label="Send" size="icon" variant="outline">
          <LuArrowRight />
        </Button>
      </ButtonGroup>
    </ButtonGroup>
  );
}

Popover

Use with a Popover component.

import { LuBot, LuChevronDown } from "react-icons/lu";
import { styled } from "styled-system/jsx";
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 default function ButtonGroupPopover() {
  return (
    <ButtonGroup>
      <Button variant="outline">
        <LuBot /> Copilot
      </Button>
      <Popover>
        <PopoverTrigger asChild>
          <Button variant="outline" size="icon" aria-label="Open Popover">
            <LuChevronDown />
          </Button>
        </PopoverTrigger>
        <PopoverContent align="end" css={{ rounded: "xl", p: "0", textStyle: "sm" }}>
          <styled.div css={{ px: "4", py: "3" }}>
            <styled.div css={{ textStyle: "sm", fontWeight: "medium" }}>Agent Tasks</styled.div>
          </styled.div>
          <Separator />
          <styled.div css={{ p: "4", textStyle: "sm", "& > p:not(:last-child)": { mb: "2" } }}>
            <Textarea
              placeholder="Describe your task in natural language."
              css={{ mb: "4", resize: "none" }}
            />
            <styled.p css={{ fontWeight: "medium" }}>Start a new task with Copilot</styled.p>
            <styled.p css={{ color: "muted.fg" }}>
              Describe your task in natural language. Copilot will work in the background and open a
              pull request for your review.
            </styled.p>
          </styled.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.

PropTypeDefault
asChildbooleanfalse
<ButtonGroup>
  <ButtonGroupText>Text</ButtonGroupText>
  <Button>Button</Button>
</ButtonGroup>

Use the asChild 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>
      <ButtonGroupText asChild>
        <Label htmlFor="name">Text</Label>
      </ButtonGroupText>
      <Input placeholder="Type something here..." id="name" />
    </ButtonGroup>
  );
}