Installation
Complete project setup first. Your configuration
must select vue/reka and include your Design System ID.
pnpm exec frontfriend download button-group -f vue --primitive rekaReview dependencies reported by the downloader and keep your source changes in version control.
Usage
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
"orientation": "horizontal",
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown> & { orientation?: "horizontal" | "vertical" };
</script>
<template>
<ButtonGroup aria-label="Button group" v-bind="args"
><Button variant="outline">Button 1</Button
><Button variant="outline">Button 2</Button></ButtonGroup
>
</template> Examples
Default
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup v-bind="args" aria-label="Button group"
><Button variant="outline">Button 1</Button
><Button variant="outline">Button 2</Button></ButtonGroup
>
</template> Orientation
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import { PlusIcon } from "lucide-vue-next";
import { MinusIcon } from "lucide-vue-next";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup orientation="vertical" aria-label="Media controls" class="h-fit"
><Button variant="outline" size="icon" aria-label="Increase"
><PlusIcon /></Button
><Button variant="outline" size="icon" aria-label="Decrease"
><MinusIcon /></Button
></ButtonGroup>
</template> Size
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<div class="flex flex-col items-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></ButtonGroup
><ButtonGroup
><Button variant="outline">Default</Button
><Button variant="outline">Button</Button
><Button variant="outline">Group</Button></ButtonGroup
><ButtonGroup
><Button variant="outline" size="lg">Large</Button
><Button variant="outline" size="lg">Button</Button
><Button variant="outline" size="lg">Group</Button></ButtonGroup
>
</div>
</template> Nested
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import { ArrowLeftIcon } from "lucide-vue-next";
import { ArrowRightIcon } from "lucide-vue-next";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup
><ButtonGroup
><Button
v-for="value in [1, 2, 3, 4, 5]"
:key="value"
variant="outline"
size="sm"
>{{ value }}</Button
></ButtonGroup
><ButtonGroup
><Button variant="outline" size="icon" aria-label="Previous"
><ArrowLeftIcon /></Button
><Button variant="outline" size="icon" aria-label="Next"
><ArrowRightIcon /></Button></ButtonGroup
></ButtonGroup>
</template> Separator
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import { ButtonGroupSeparator } from "@/components/ui/button-group";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup
><Button variant="secondary" size="sm">Copy</Button
><ButtonGroupSeparator /><Button variant="secondary" size="sm"
>Paste</Button
></ButtonGroup
>
</template> Split
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import { ButtonGroupSeparator } from "@/components/ui/button-group";
import { PlusIcon } from "lucide-vue-next";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup
><Button variant="secondary">Button</Button><ButtonGroupSeparator /><Button
size="icon"
variant="secondary"
aria-label="Add"
><PlusIcon /></Button
></ButtonGroup>
</template> With Input
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { SearchIcon } from "lucide-vue-next";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup
><Input placeholder="Search..." /><Button
variant="outline"
aria-label="Search"
><SearchIcon /></Button
></ButtonGroup>
</template> With Input Group
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import { PlusIcon } from "lucide-vue-next";
import { InputGroup } from "@/components/ui/input-group";
import { InputGroupInput } from "@/components/ui/input-group";
import { InputGroupAddon } from "@/components/ui/input-group";
import { InputGroupButton } from "@/components/ui/input-group";
import { ShareIcon } from "lucide-vue-next";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup class="[--radius:9999rem]"
><ButtonGroup
><Button variant="outline" size="icon" aria-label="Add"
><PlusIcon /></Button></ButtonGroup
><ButtonGroup
><InputGroup
><InputGroupInput placeholder="Send a message..." /><InputGroupAddon
align="inline-end"
><InputGroupButton size="icon-xs" aria-label="Share"
><ShareIcon /></InputGroupButton></InputGroupAddon></InputGroup></ButtonGroup
></ButtonGroup>
</template> Dropdown
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import { DropdownMenu } from "@/components/ui/dropdown-menu";
import { DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { ChevronDownIcon } from "lucide-vue-next";
import { DropdownMenuContent } from "@/components/ui/dropdown-menu";
import { DropdownMenuGroup } from "@/components/ui/dropdown-menu";
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
import { VolumeOffIcon } from "lucide-vue-next";
import { CheckIcon } from "lucide-vue-next";
import { ShareIcon } from "lucide-vue-next";
import { CopyIcon } from "lucide-vue-next";
import { DropdownMenuSeparator } from "@/components/ui/dropdown-menu";
import { TrashIcon } from "lucide-vue-next";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup
><Button variant="outline">Follow</Button
><DropdownMenu
><DropdownMenuTrigger as-child
><Button variant="outline" class="pl-2" aria-label="More"
><ChevronDownIcon /></Button></DropdownMenuTrigger
><DropdownMenuContent align="end" class="[--radius:1rem]"
><DropdownMenuGroup
><DropdownMenuItem
><VolumeOffIcon />Mute Conversation</DropdownMenuItem
><DropdownMenuItem><CheckIcon />Mark as Read</DropdownMenuItem
><DropdownMenuItem><ShareIcon />Share Conversation</DropdownMenuItem
><DropdownMenuItem
><CopyIcon />Copy Conversation</DropdownMenuItem
></DropdownMenuGroup
><DropdownMenuSeparator /><DropdownMenuGroup
><DropdownMenuItem variant="destructive"
><TrashIcon />Delete Conversation</DropdownMenuItem
></DropdownMenuGroup
></DropdownMenuContent
></DropdownMenu
></ButtonGroup
>
</template> Select Pairing
<script setup lang="ts">
import { ref } from "vue";
import { ButtonGroup } from "@/components/ui/button-group";
import { Select } from "@/components/ui/select";
import { SelectTrigger } from "@/components/ui/select";
import { SelectContent } from "@/components/ui/select";
import { SelectItem } from "@/components/ui/select";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ArrowRightIcon } from "lucide-vue-next";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
const currency = ref("$");
</script>
<template>
<ButtonGroup
><ButtonGroup
><Select v-model="currency"
><SelectTrigger class="font-mono">{{ currency }}</SelectTrigger
><SelectContent class="min-w-24"
><SelectItem value="$"
>$ <span class="text-neutral-mid">US Dollar</span></SelectItem
><SelectItem value="€"
>€ <span class="text-neutral-mid">Euro</span></SelectItem
><SelectItem value="£"
>£ <span class="text-neutral-mid">British Pound</span></SelectItem
></SelectContent
></Select
><Input placeholder="10.00" pattern="[0-9]*" /></ButtonGroup
><ButtonGroup
><Button aria-label="Send" size="icon" variant="outline"
><ArrowRightIcon /></Button></ButtonGroup
></ButtonGroup>
</template> Popover Pairing
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { Button } from "@/components/ui/button";
import { BotIcon } from "lucide-vue-next";
import { Popover } from "@/components/ui/popover";
import { PopoverTrigger } from "@/components/ui/popover";
import { ChevronDownIcon } from "lucide-vue-next";
import { PopoverContent } from "@/components/ui/popover";
import { Textarea } from "@/components/ui/textarea";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup
><Button variant="outline"><BotIcon /> Copilot</Button
><Popover
><PopoverTrigger as-child
><Button variant="outline" size="icon" aria-label="Open Popover"
><ChevronDownIcon /></Button></PopoverTrigger
><PopoverContent align="end" class="rounded-xl p-0 text-sm"
><div class="px-4 py-3">
<div class="text-sm font-medium">Agent Tasks</div>
</div>
<div class="border-t border-neutral-subtle p-4 text-sm">
<Textarea
placeholder="Describe your task in natural language."
class="mb-4 resize-none"
/>
<p class="font-medium">Start a new task with Copilot</p>
<p class="text-neutral-mid">
Describe your task in natural language.
</p>
</div></PopoverContent
></Popover
></ButtonGroup
>
</template> Text As Child
<script setup lang="ts">
import { ButtonGroup } from "@/components/ui/button-group";
import { ButtonGroupText } from "@/components/ui/button-group";
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import * as UI from "@/components/ui/button-group";
const Component = UI.ButtonGroup;
const args = {
} satisfies Partial<
InstanceType<typeof Component> extends { $props: infer Props } ? Props : {}
> &
Record<string, unknown>;
</script>
<template>
<ButtonGroup
><ButtonGroupText as-child
><Label for="url">https://</Label></ButtonGroupText
><Input id="url" placeholder="frontfriend" /><ButtonGroupText
>.dev</ButtonGroupText
></ButtonGroup
>
</template> API reference
Frontfriend-owned props. Native HTML and inherited primitive props remain documented by Reka UI.
ButtonGroup
| Prop | Type | Default | Description |
|---|---|---|---|
class | HTMLAttributes["class"] | — | |
orientation | ButtonGroupVariants["orientation"] | — |
ButtonGroupSeparator
| Prop | Type | Default | Description |
|---|---|---|---|
class | HTMLAttributes["class"] | — |
ButtonGroupText
| Prop | Type | Default | Description |
|---|---|---|---|
as | keyof HTMLElementTagNameMap | — | |
asChild | boolean | — | |
class | HTMLAttributes["class"] | — | |
orientation | "horizontal" | "vertical" | — |
Configuration determines the actual classes and available styling hooks. See Theming & component configuration before introducing a repeated local override.