diff --git a/.changeset/button-group.md b/.changeset/button-group.md
new file mode 100644
index 000000000..5efe035f0
--- /dev/null
+++ b/.changeset/button-group.md
@@ -0,0 +1,5 @@
+---
+"@cloudflare/kumo": minor
+---
+
+Add `ButtonGroup` component for joining a small set of tightly-coupled buttons into a single control — most commonly a split button (a primary action next to a dropdown trigger). Handles layout only: children keep their own variant, size, and shape while the group flattens inner corners and overlaps borders so the buttons share one seam. Renders `role="group"`. For grouping multiple independent buttons or inputs, use `Toolbar` instead.
diff --git a/packages/kumo-docs-astro/src/components/SidebarNav.tsx b/packages/kumo-docs-astro/src/components/SidebarNav.tsx
index 5d79adcb7..852d63b0e 100644
--- a/packages/kumo-docs-astro/src/components/SidebarNav.tsx
+++ b/packages/kumo-docs-astro/src/components/SidebarNav.tsx
@@ -44,6 +44,7 @@ const componentItems: NavItem[] = [
{ label: "Banner", href: "/components/banner" },
{ label: "Breadcrumbs", href: "/components/breadcrumbs" },
{ label: "Button", href: "/components/button" },
+ { label: "Button Group", href: "/components/button-group" },
{ label: "Checkbox", href: "/components/checkbox" },
{ label: "Clipboard Text", href: "/components/clipboard-text" },
{ label: "Cloudflare Logo", href: "/components/cloudflare-logo" },
diff --git a/packages/kumo-docs-astro/src/components/demos/ButtonGroupDemo.tsx b/packages/kumo-docs-astro/src/components/demos/ButtonGroupDemo.tsx
new file mode 100644
index 000000000..1e585ffad
--- /dev/null
+++ b/packages/kumo-docs-astro/src/components/demos/ButtonGroupDemo.tsx
@@ -0,0 +1,99 @@
+import { Button, ButtonGroup, DropdownMenu } from "@cloudflare/kumo";
+import { CaretDownIcon } from "@phosphor-icons/react";
+
+/**
+ * Split button: a primary action joined with a dropdown trigger for related
+ * secondary actions. The caret button uses `shape="square"` and an
+ * `aria-label`.
+ */
+export function ButtonGroupSplitDemo() {
+ return (
+
+
+
+
+
+
+ }
+ />
+
+ Deploy to staging
+ Deploy and tail logs
+ Schedule deploy…
+
+
+
+ );
+}
+
+/**
+ * Split buttons work with any button variant — here the secondary style for a
+ * lower-emphasis action.
+ */
+export function ButtonGroupSecondaryDemo() {
+ return (
+
+
+
+
+
+
+ }
+ />
+
+ Save as draft
+ Save and publish
+ Save a copy…
+
+
+
+ );
+}
+
+/**
+ * Match the `size` on both buttons to keep the split button aligned.
+ */
+export function ButtonGroupSizesDemo() {
+ const sizes = ["sm", "base", "lg"] as const;
+ return (
+
+ );
+}
diff --git a/packages/kumo-docs-astro/src/components/demos/HomeGrid.tsx b/packages/kumo-docs-astro/src/components/demos/HomeGrid.tsx
index 10d82219d..50b3025b8 100644
--- a/packages/kumo-docs-astro/src/components/demos/HomeGrid.tsx
+++ b/packages/kumo-docs-astro/src/components/demos/HomeGrid.tsx
@@ -4,6 +4,7 @@ import {
Badge,
Banner,
Button,
+ ButtonGroup,
Checkbox,
ClipboardText,
Collapsible,
@@ -43,6 +44,7 @@ import { ShikiProvider, CodeHighlighted } from "@cloudflare/kumo/code";
import { CommandPaletteBasicDemo } from "~/components/demos/CommandPaletteDemo";
import { InputGroupDemo } from "~/components/demos/InputGroupDemo";
import {
+ CaretDownIcon,
MagnifyingGlassIcon,
PlusIcon,
TranslateIcon,
@@ -56,6 +58,7 @@ const componentRoutes: Record = {
banner: "/components/banner",
breadcrumbs: "/components/breadcrumbs",
button: "/components/button",
+ "button-group": "/components/button-group",
checkbox: "/components/checkbox",
"clipboard-text": "/components/clipboard-text",
"code-highlighted": "/components/code-highlighted",
@@ -138,6 +141,18 @@ export function HomeGrid() {
),
},
+ {
+ name: "Button Group",
+ id: "button-group",
+ Component: (
+
+
+
+
+ ),
+ },
{
name: "Input",
id: "input",
diff --git a/packages/kumo-docs-astro/src/pages/components/button-group.mdx b/packages/kumo-docs-astro/src/pages/components/button-group.mdx
new file mode 100644
index 000000000..633450e17
--- /dev/null
+++ b/packages/kumo-docs-astro/src/pages/components/button-group.mdx
@@ -0,0 +1,152 @@
+---
+layout: ~/layouts/MdxDocLayout.astro
+title: "Button Group"
+description: "Joins a primary action and a dropdown trigger into a single split button."
+sourceFile: "components/button-group"
+---
+
+import ComponentExample from "~/components/docs/ComponentExample.astro";
+import ComponentSection from "~/components/docs/ComponentSection.astro";
+import CodeBlock from "~/components/docs/CodeBlock.astro";
+import PropsTable from "~/components/docs/PropsTable.astro";
+import {
+ ButtonGroupSplitDemo,
+ ButtonGroupSecondaryDemo,
+ ButtonGroupSizesDemo,
+} from "~/components/demos/ButtonGroupDemo";
+
+{/* Demo */}
+
+
+
+
+
+
+
+{/* When to use */}
+
+
+
+## When to use
+
+
+ `ButtonGroup` joins a small set of tightly-coupled buttons into a single
+ control — most commonly a **split button**: a primary action next to a
+ dropdown trigger for related, secondary actions.
+
+
+
+ Grouping multiple independent buttons or inputs? Use{" "}
+ Toolbar instead. A toolbar (e.g. a
+ formatting bar or a page-level set of actions) needs roaming-focus keyboard
+ semantics that `ButtonGroup` intentionally does not provide.
+
+ `ButtonGroup` is a layout wrapper. Its children keep their own `variant`,
+ `size`, and `shape` — the group only flattens the inner corners and overlaps
+ borders so the buttons share a single seam. Give the group an `aria-label`
+ describing the action, and give the dropdown trigger its own `aria-label`.
+