Button Group
A container that groups related buttons together with consistent styling.
<twig:ButtonGroup>
<twig:ButtonGroup class="hidden sm:flex">
<twig:Button variant="outline" size="icon" aria-label="Go back">
<twig:ux:icon name="lucide:arrow-left" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">Archive</twig:Button>
<twig:Button variant="outline">Report</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">
<twig:ux:icon name="lucide:clock" />
Snooze
</twig:Button>
<twig:Button variant="outline" size="icon" aria-label="More options">
<twig:ux:icon name="lucide:more-horizontal" />
</twig:Button>
</twig:ButtonGroup>
</twig:ButtonGroup>
Installation
bin/console ux:install button-group --kit shadcn
That's it!
Install the following Composer dependencies:
composer require twig/extra-bundle twig/html-extra:^3.12.0 tales-from-a-dev/twig-tailwind-extra:^1.0.0
Copy the following file(s) into your Symfony app:
{# @prop orientation 'horizontal'|'vertical' The layout direction of the button group. Defaults to `horizontal` #}
{# @block content The grouped buttons and/or separators #}
{%- props orientation = 'horizontal' -%}
{%- set style = html_cva(
base: 'group/button-group flex w-fit items-stretch *:focus-visible:relative *:focus-visible:z-10 has-[>[data-slot=button-group]]:gap-2 ltr:has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-r-lg rtl:has-[select[aria-hidden=true]:last-child]:[&>[data-slot=select-trigger]:last-of-type]:rounded-e-lg [&>[data-slot=select-trigger]:not([class*=\'w-\'])]:w-fit [&>input]:flex-1 ',
variants: {
orientation: {
horizontal: 'ltr:[&>*:not(:first-child)]:rounded-l-none rtl:[&>*:not(:first-child)]:rounded-s-none ltr:[&>*:not(:first-child)]:border-l-0 rtl:[&>*:not(:first-child)]:border-s-0 ltr:[&>*:not(:last-child)]:rounded-r-none rtl:[&>*:not(:last-child)]:rounded-e-none ltr:[&>[data-slot]:not(:has(~[data-slot]))]:rounded-r-lg! rtl:[&>[data-slot]:not(:has(~[data-slot]))]:rounded-e-lg!',
vertical: 'flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>*:not(:last-child)]:rounded-b-none [&>[data-slot]:not(:has(~[data-slot]))]:rounded-b-lg!',
},
},
default_variant: {
orientation: 'horizontal',
},
) -%}
<div
role="group"
data-slot="button-group"
data-orientation="{{ orientation }}"
class="{{ style.apply({orientation: orientation}, attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</div>
{# @prop orientation 'horizontal'|'vertical' The separator orientation. Defaults to `vertical` #}
{# @block content Optional custom separator content #}
{%- props orientation = 'vertical' -%}
<twig:Separator
orientation="{{ orientation }}"
data-slot="button-group-separator"
class="{{ ('relative self-stretch bg-input ' ~ (orientation == 'vertical' ? 'my-px h-auto' : 'mx-px w-auto') ~ ' ' ~ attributes.render('class'))|tailwind_merge }}"
{{ ...attributes }}
/>
{# @prop as 'div' The HTML tag to render. Defaults to `div` #}
{# @block content The text content displayed in the button group #}
{%- props as = 'div' -%}
<{{ as }}
data-slot="button-group-text"
class="{{ ('flex items-center gap-2 rounded-lg border bg-muted px-2.5 text-sm font-medium [&_svg]:pointer-events-none [&_svg:not([class*=\'size-\'])]:size-4 ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes }}
>
{%- block content %}{% endblock -%}
</{{ as }}>
{# @prop orientation 'horizontal'|'vertical' The separator orientation. Defaults to `horizontal` #}
{# @prop decorative boolean Whether the separator is purely decorative (not semantic). Defaults to `true` #}
{%- props orientation = 'horizontal', decorative = true -%}
<div
class="{{ ('shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({
'data-slot': 'separator',
'data-orientation': orientation,
role: decorative ? 'none' : 'separator',
'aria-orientation': decorative ? false : orientation,
}) }}
></div>
Happy coding!
Usage
<twig:ButtonGroup>
<twig:Button>Button 1</twig:Button>
<twig:Button>Button 2</twig:Button>
</twig:ButtonGroup>
Accessibility
- The
ButtonGroupcomponent has theroleattribute set togroup. - Use
Tabto navigate between the buttons in the group. - Use
aria-labeloraria-labelledbyto label the button group.
Examples
Orientation
Set the orientation prop to change the button group layout.
<twig:ButtonGroup orientation="vertical" aria-label="Media controls" class="h-fit">
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:plus" />
</twig:Button>
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:minus" />
</twig:Button>
</twig:ButtonGroup>
Size
Control the size of buttons using the size prop on individual buttons.
<div class="flex flex-col items-start gap-8">
<twig:ButtonGroup>
<twig:Button variant="outline" size="sm">Small</twig:Button>
<twig:Button variant="outline" size="sm">Button</twig:Button>
<twig:Button variant="outline" size="sm">Group</twig:Button>
<twig:Button variant="outline" size="icon-sm">
<twig:ux:icon name="lucide:plus" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">Default</twig:Button>
<twig:Button variant="outline">Button</twig:Button>
<twig:Button variant="outline">Group</twig:Button>
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:plus" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline" size="lg">Large</twig:Button>
<twig:Button variant="outline" size="lg">Button</twig:Button>
<twig:Button variant="outline" size="lg">Group</twig:Button>
<twig:Button variant="outline" size="icon-lg">
<twig:ux:icon name="lucide:plus" />
</twig:Button>
</twig:ButtonGroup>
</div>
Nested
Nest ButtonGroup components to create button groups with spacing.
<twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:plus" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:InputGroup>
<twig:InputGroup:Input placeholder="Send a message..." />
<twig:InputGroup:Addon align="inline-end">
<twig:ux:icon name="lucide:audio-lines" />
</twig:InputGroup:Addon>
</twig:InputGroup>
</twig:ButtonGroup>
</twig:ButtonGroup>
Separator
The ButtonGroup:Separator 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.
<twig:ButtonGroup>
<twig:Button variant="secondary" size="sm">Copy</twig:Button>
<twig:ButtonGroup:Separator />
<twig:Button variant="secondary" size="sm">Paste</twig:Button>
</twig:ButtonGroup>
Split
Create a split button group by adding two buttons separated by a ButtonGroup:Separator.
<twig:ButtonGroup>
<twig:Button variant="secondary">Button</twig:Button>
<twig:ButtonGroup:Separator />
<twig:Button size="icon" variant="secondary">
<twig:ux:icon name="tabler:plus" />
</twig:Button>
</twig:ButtonGroup>
Input
Wrap an Input component with buttons.
<twig:ButtonGroup>
<twig:Input placeholder="Search..." />
<twig:Button variant="outline" aria-label="Search">
<twig:ux:icon name="lucide:search" />
</twig:Button>
</twig:ButtonGroup>
Input Group
Wrap an InputGroup component to create complex input layouts.
<twig:ButtonGroup class="[--radius:9999rem]">
<twig:ButtonGroup>
<twig:Button variant="outline" size="icon">
<twig:ux:icon name="lucide:plus" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:InputGroup>
<twig:InputGroup:Input placeholder="Send a message..." />
<twig:InputGroup:Addon align="inline-end">
<twig:ux:icon name="lucide:audio-lines" />
</twig:InputGroup:Addon>
</twig:InputGroup>
</twig:ButtonGroup>
</twig:ButtonGroup>
RTL
To enable RTL support, set the dir="rtl" attribute on the root element.
<div class="flex flex-col gap-6">
<div dir="rtl">
<twig:ButtonGroup>
<twig:ButtonGroup class="hidden sm:flex">
<twig:Button variant="outline" size="icon" aria-label="رجوع">
<twig:ux:icon name="lucide:arrow-right" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">أرشفة</twig:Button>
<twig:Button variant="outline">تقرير</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">تأجيل</twig:Button>
<twig:Button variant="outline" size="icon" aria-label="المزيد">
<twig:ux:icon name="lucide:more-horizontal" />
</twig:Button>
</twig:ButtonGroup>
</twig:ButtonGroup>
</div>
<div dir="rtl">
<twig:ButtonGroup>
<twig:ButtonGroup class="hidden sm:flex">
<twig:Button variant="outline" size="icon" aria-label="חזרה">
<twig:ux:icon name="lucide:arrow-right" />
</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">ארכיון</twig:Button>
<twig:Button variant="outline">דוח</twig:Button>
</twig:ButtonGroup>
<twig:ButtonGroup>
<twig:Button variant="outline">דחה</twig:Button>
<twig:Button variant="outline" size="icon" aria-label="עוד">
<twig:ux:icon name="lucide:more-horizontal" />
</twig:Button>
</twig:ButtonGroup>
</twig:ButtonGroup>
</div>
</div>
API Reference
Component ButtonGroup
| Prop | Type | Description |
|---|---|---|
orientation |
'horizontal'|'vertical' |
The layout direction of the button group. Defaults to horizontal |
| Block | Description |
|---|---|
content |
The grouped buttons and/or separators |
Component ButtonGroup:Separator
| Prop | Type | Description |
|---|---|---|
orientation |
'horizontal'|'vertical' |
The separator orientation. Defaults to vertical |
| Block | Description |
|---|---|
content |
Optional custom separator content |
Component ButtonGroup:Text
| Prop | Type | Description |
|---|---|---|
as |
'div' |
The HTML tag to render. Defaults to div |
| Block | Description |
|---|---|
content |
The text content displayed in the button group |