Skip to content
Tier

Button group

Pro

Buttons joined into one control: shared edges, inner corners squared, one border between each pair. Use it for a few related actions that read as a set (a split button, Previous and Next, zoom out and in). Each button stays its own tab stop, as buttons are: a button group has no arrow-key model (a toolbar does).

The group is role="group"; give it a label when its buttons don't explain themselves together ("Zoom", "Pagination"). The joins use logical sides, so a row joins correctly right to left, and they reach a button inside Locked too, so a locked button keeps its place in the join and says why.

Markdown
Version 5.2.0· other versions

This page is for 5.2.0, the current release. An earlier release’s page is the Markdown that release published, so the docs match the version you have installed (candor-lock.json records it).

Not in the docs of 1.3.0, 1.4.0, 1.5.0, 1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.8.1, 1.9.0, 1.9.1, 2.0.0, 2.1.0, 2.2.0, 2.3.0, 2.4.0, 2.5.0, 2.5.1, 2.6.0, 2.6.1, 2.6.2, 2.7.0, 2.8.0, 2.9.0, 2.10.0, 3.0.0, 4.0.0, 4.0.1, 4.0.2, 4.1.0 and 4.2.0: the page came later, or had another name then.

Docs pages weren’t kept for 1.0.0, 1.0.1, 1.1.0, 1.1.1, 1.2.0 and 1.2.1: they are kept from 1.3.0 on, and before that only llms.txt was published.

Preview

Loading the preview

Install

With the Candor CLI

First time? Get started sets up the registry and theme. Or with shadcn directly: npx shadcn@latest add @candor/button-group

Brings in: separator, tokens, utils · npm: @base-ui/react@^1.9.0

Usage

TSX · The example above
"use client"

import { ChevronLeft, ChevronRight, Minus, Plus } from "lucide-react"
import { Button } from "@/components/candor/ui/button"
import { ButtonGroup, ButtonGroupText } from "@/components/candor/ui/button-group"
import { Locked } from "@/components/candor/locked"
import { TooltipProvider } from "@/components/candor/ui/tooltip"

// Name a group whose buttons don't explain themselves together. A locked button keeps its place in the join and says
// why. Each button is its own tab stop; for arrow keys between buttons, use a toolbar.
export default function Example() {
  return (
    <TooltipProvider>
      <div className="flex flex-wrap items-center gap-4">
        <ButtonGroup label="Pages">
          <Button variant="outline">
            <ChevronLeft aria-hidden />
            Previous
          </Button>
          <ButtonGroupText>Page 5 of 5</ButtonGroupText>
          <Locked reason="This is the last page">
            <Button variant="outline">
              Next
              <ChevronRight aria-hidden />
            </Button>
          </Locked>
        </ButtonGroup>
        <ButtonGroup label="Zoom">
          <Button variant="outline" size="icon" aria-label="Zoom out">
            <Minus aria-hidden />
          </Button>
          <Button variant="outline" size="icon" aria-label="Zoom in">
            <Plus aria-hidden />
          </Button>
        </ButtonGroup>
      </div>
    </TooltipProvider>
  )
}

<ButtonGroup>

PropTypeDefaultDescription
labelstring | undefined—The group's name, read before its buttons. Give one when the buttons don't explain themselves together.
orientation"horizontal" | "vertical" | undefined"horizontal"Whether the buttons sit in a row or a column. Defaults to a row.
classNamestring | undefined—Classes, merged with Candor's.
childrenrequiredReact.ReactNode—The buttons, and any `ButtonGroupSeparator` or `ButtonGroupText` between them.

<ButtonGroupSeparator>

A line between two parts of a ButtonGroup, for buttons drawn without a border of their own (variant="secondary" or "ghost"). It runs across the group, following the group's orientation: vertical in a row, horizontal in a column. It is decoration only.

PropTypeDefaultDescription
orientation"horizontal" | "vertical" | undefined—Which way the line runs. Defaults to across the group: `vertical` in a row, `horizontal` in a column.
classNamestring | undefined—Classes, merged with Candor's.

<ButtonGroupText>

A cell of text in a ButtonGroup's style: a unit, a label or a count joined to the buttons beside it ("Page 2 of 5"). It is a div by default; render draws another element (a <label> for an input in the group) with its styles.

PropTypeDefaultDescription
renderuseRender.RenderProp | undefined—Draws this element in place of the `div`, with the cell's styles: `render={<label htmlFor="q" />}`.
classNamestring | undefined—Classes, merged with Candor's.
childrenReact.ReactNode | undefined—The text, and an icon if it helps.