Run the CLI
Use the CLI to add the component to your project.
npx @ngzard/ui@latest add button-groupA container that groups related buttons together with consistent styling.
import { Component } from '@angular/core';
import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardButtonGroupComponent } from '@/shared/components/button-group/button-group.component';
import { ZardDividerComponent } from '@/shared/components/divider';
import { ZardIconComponent } from '@/shared/components/icon';
import { ZardMenuImports } from '@/shared/components/menu/menu.imports';
@Component({
selector: 'z-demo-button-group-default',
imports: [ZardButtonGroupComponent, ZardButtonComponent, ZardIconComponent, ZardMenuImports, ZardDividerComponent],
template: `
<z-button-group>
<z-button-group class="hidden sm:flex">
<button type="button" z-button zType="outline" aria-label="Go Back">
<i z-icon zType="arrow-left"></i>
</button>
</z-button-group>
<z-button-group>
<button type="button" z-button zType="outline">Archive</button>
<button type="button" z-button zType="outline">Report</button>
</z-button-group>
<z-button-group>
<button type="button" z-button zType="outline">Snooze</button>
<button type="button" z-button zType="outline" z-menu [zMenuTriggerFor]="menu">
<i z-icon zType="ellipsis"></i>
<ng-template #menu>
<div z-menu-content class="w-48">
<button type="button" z-menu-item>
<i z-icon zType="check"></i>
Mark as Read
</button>
<button type="button" z-menu-item>
<i z-icon zType="archive"></i>
Archive
</button>
<z-divider zSpacing="sm" />
<button type="button" z-menu-item>
<i z-icon zType="clock"></i>
Snooze
</button>
<button type="button" z-menu-item>
<i z-icon zType="calendar-plus"></i>
Add to Calendar
</button>
<button type="button" z-menu-item>
<i z-icon zType="list-filter-plus"></i>
Add to List
</button>
<button
type="button"
z-menu-item
z-menu
[zMenuTriggerFor]="subMenu"
zPlacement="rightTop"
class="justify-between"
>
<div class="flex items-center">
<i z-icon zType="tag" class="mr-1"></i>
Label as
</div>
<i z-icon zType="chevron-right"></i>
<ng-template #subMenu>
<div z-menu-content class="w-48">
<button type="button" z-menu-item>Personal</button>
<button type="button" z-menu-item>Work</button>
<button type="button" z-menu-item>Other</button>
</div>
</ng-template>
</button>
<z-divider zSpacing="sm" />
<button type="button" z-menu-item class="text-red-500">
<i z-icon zType="trash"></i>
Trash
</button>
</div>
</ng-template>
</button>
</z-button-group>
</z-button-group>
`,
})
export class ZardDemoButtonGroupDefaultComponent {}
Use the CLI to add the component to your project.
npx @ngzard/ui@latest add button-grouppnpm dlx @ngzard/ui@latest add button-groupyarn dlx @ngzard/ui@latest add button-groupbunx @ngzard/ui@latest add button-groupCreate the component directory structure and add the following files to your project.
import {
ChangeDetectionStrategy,
Component,
computed,
Directive,
inject,
input,
ViewEncapsulation,
} from '@angular/core';
import { type ClassValue } from 'clsx';
import {
buttonGroupDividerVariants,
buttonGroupTextVariants,
buttonGroupVariants,
type ZardButtonGroupVariants,
} from './button-group.variants';
import { ZardDividerComponent } from '../divider/divider.component';
import { type ZardDividerVariants } from '../divider/divider.variants';
import { mergeClasses } from '@/shared/utils/merge-classes';
@Component({
selector: 'z-button-group',
template: `
<ng-content />
`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
role: 'group',
'[class]': 'classes()',
'[attr.aria-orientation]': 'zOrientation()',
},
exportAs: 'zButtonGroup',
})
export class ZardButtonGroupComponent {
readonly zOrientation = input<Required<ZardButtonGroupVariants>['zOrientation']>('horizontal');
readonly class = input<ClassValue>('');
protected readonly classes = computed(() =>
mergeClasses(
buttonGroupVariants({
zOrientation: this.zOrientation(),
}),
this.class(),
),
);
}
@Component({
selector: 'z-button-group-divider',
imports: [ZardDividerComponent],
template: `
<z-divider [class]="classes()" zSpacing="none" aria-hidden="true" [zOrientation]="orientation()" />
`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
class: 'contents',
},
exportAs: 'zButtonGroupDivider',
})
export class ZardButtonGroupDividerComponent {
readonly zOrientation = input<ZardDividerVariants['zOrientation']>(null);
readonly class = input<ClassValue>('');
private readonly parent = inject(ZardButtonGroupComponent, {
optional: true,
host: true,
});
protected readonly orientation = computed(() => {
if (!this.parent || typeof this.zOrientation() === 'string') {
return this.zOrientation();
}
return this.parent.zOrientation() === 'vertical' ? 'horizontal' : 'vertical';
});
protected readonly classes = computed(() =>
mergeClasses(
buttonGroupDividerVariants({
zOrientation: this.orientation(),
}),
this.class(),
),
);
}
@Directive({
selector: '[z-button-group-text]',
host: {
'[class]': 'classes()',
},
exportAs: 'zButtonGroupText',
})
export class ZardButtonGroupTextDirective {
readonly class = input<ClassValue>('');
protected readonly classes = computed(() => mergeClasses(buttonGroupTextVariants(), this.class()));
}
import { cva, type VariantProps } from 'class-variance-authority';
export const buttonGroupVariants = cva(
'flex w-fit items-stretch [&>*]:focus-visible:z-10 [&>*]:focus-visible:relative has-[>z-button-group]:gap-2',
{
variants: {
zOrientation: {
horizontal:
'[&>*:not(:first-child)]:rounded-l-none [&>*:not(:first-child)]:border-l-0 [&>z-select:not(:first-child)>button]:rounded-l-none [&>z-select:not(:first-child)>button]:border-l-0 [&>*:not(:last-child)]:rounded-r-none [&>z-select:not(:last-child)>button]:rounded-r-none [&>input]:h-9 [&>input]:py-0',
vertical:
'flex-col [&>*:not(:first-child)]:rounded-t-none [&>*:not(:first-child)]:border-t-0 [&>z-select:not(:first-child)>button]:rounded-t-none [&>z-select:not(:first-child)>button]:border-t-0 [&>*:not(:last-child)]:rounded-b-none [&>z-select:not(:last-child)>button]:rounded-b-none',
},
},
defaultVariants: {
zOrientation: 'horizontal',
},
},
);
export type ZardButtonGroupVariants = VariantProps<typeof buttonGroupVariants>;
export const buttonGroupDividerVariants = cva(
'bg-input relative self-stretch grow-0 shrink-0 pointer-events-none select-none',
{
variants: {
zOrientation: {
horizontal: 'w-auto',
vertical: 'h-auto',
},
},
},
);
export const buttonGroupTextVariants = cva(
"bg-muted flex items-center gap-2 rounded-md border h-9 px-4 text-sm font-medium shadow-xs [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
);
export * from './button-group.component';
export * from './button-group.variants';
import { Component } from '@angular/core';
import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardButtonGroupComponent } from '@/shared/components/button-group/button-group.component';
import { ZardDividerComponent } from '@/shared/components/divider';
import { ZardIconComponent } from '@/shared/components/icon';
import { ZardMenuImports } from '@/shared/components/menu/menu.imports';
@Component({
selector: 'z-demo-button-group-default',
imports: [ZardButtonGroupComponent, ZardButtonComponent, ZardIconComponent, ZardMenuImports, ZardDividerComponent],
template: `
<z-button-group>
<z-button-group class="hidden sm:flex">
<button type="button" z-button zType="outline" aria-label="Go Back">
<i z-icon zType="arrow-left"></i>
</button>
</z-button-group>
<z-button-group>
<button type="button" z-button zType="outline">Archive</button>
<button type="button" z-button zType="outline">Report</button>
</z-button-group>
<z-button-group>
<button type="button" z-button zType="outline">Snooze</button>
<button type="button" z-button zType="outline" z-menu [zMenuTriggerFor]="menu">
<i z-icon zType="ellipsis"></i>
<ng-template #menu>
<div z-menu-content class="w-48">
<button type="button" z-menu-item>
<i z-icon zType="check"></i>
Mark as Read
</button>
<button type="button" z-menu-item>
<i z-icon zType="archive"></i>
Archive
</button>
<z-divider zSpacing="sm" />
<button type="button" z-menu-item>
<i z-icon zType="clock"></i>
Snooze
</button>
<button type="button" z-menu-item>
<i z-icon zType="calendar-plus"></i>
Add to Calendar
</button>
<button type="button" z-menu-item>
<i z-icon zType="list-filter-plus"></i>
Add to List
</button>
<button
type="button"
z-menu-item
z-menu
[zMenuTriggerFor]="subMenu"
zPlacement="rightTop"
class="justify-between"
>
<div class="flex items-center">
<i z-icon zType="tag" class="mr-1"></i>
Label as
</div>
<i z-icon zType="chevron-right"></i>
<ng-template #subMenu>
<div z-menu-content class="w-48">
<button type="button" z-menu-item>Personal</button>
<button type="button" z-menu-item>Work</button>
<button type="button" z-menu-item>Other</button>
</div>
</ng-template>
</button>
<z-divider zSpacing="sm" />
<button type="button" z-menu-item class="text-red-500">
<i z-icon zType="trash"></i>
Trash
</button>
</div>
</ng-template>
</button>
</z-button-group>
</z-button-group>
`,
})
export class ZardDemoButtonGroupDefaultComponent {}
import { Component } from '@angular/core';
import { ZardButtonComponent } from '../../button/button.component';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardButtonGroupComponent } from '../button-group.component';
@Component({
selector: 'z-demo-button-group-orientation',
imports: [ZardButtonGroupComponent, ZardButtonComponent, ZardIconComponent],
template: `
<z-button-group zOrientation="vertical">
<button z-button zType="outline"><i z-icon zType="plus"></i></button>
<button z-button zType="outline"><i z-icon zType="minus"></i></button>
</z-button-group>
`,
})
export class ZardDemoButtonGroupOrientationComponent {}
import { Component } from '@angular/core';
import { ZardButtonComponent } from '../../button/button.component';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardButtonGroupComponent } from '../button-group.component';
@Component({
selector: 'z-demo-button-group-size',
imports: [ZardButtonGroupComponent, ZardButtonComponent, ZardIconComponent],
template: `
<z-button-group>
<button z-button zType="outline" zSize="sm">Small</button>
<button z-button zType="outline" zSize="sm">Group</button>
<button z-button zType="outline" zSize="sm"><i z-icon zType="plus"></i></button>
</z-button-group>
<z-button-group>
<button z-button zType="outline">Default</button>
<button z-button zType="outline">Group</button>
<button z-button zType="outline"><i z-icon zType="plus"></i></button>
</z-button-group>
<z-button-group>
<button z-button zType="outline" zSize="lg">Large</button>
<button z-button zType="outline" zSize="lg">Group</button>
<button z-button zType="outline" zSize="lg"><i z-icon zType="plus"></i></button>
</z-button-group>
`,
host: {
class: 'flex flex-col items-start gap-4',
},
})
export class ZardDemoButtonGroupSizeComponent {}
import { Component } from '@angular/core';
import { ZardButtonComponent } from '../../button/button.component';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardButtonGroupComponent } from '../button-group.component';
@Component({
selector: 'z-demo-button-group-nested',
imports: [ZardButtonGroupComponent, ZardButtonComponent, ZardIconComponent],
template: `
<z-button-group>
<z-button-group>
<button z-button zSize="sm" zType="outline">1</button>
<button z-button zSize="sm" zType="outline">2</button>
<button z-button zSize="sm" zType="outline">3</button>
<button z-button zSize="sm" zType="outline">4</button>
<button z-button zSize="sm" zType="outline">5</button>
<button z-button zSize="sm" zType="outline">6</button>
</z-button-group>
<z-button-group>
<button z-button zSize="sm" zType="outline"><i z-icon zType="arrow-left"></i></button>
<button z-button zSize="sm" zType="outline"><i z-icon zType="arrow-right"></i></button>
</z-button-group>
</z-button-group>
`,
})
export class ZardDemoButtonGroupNestedComponent {}
import { Component } from '@angular/core';
import { ZardButtonComponent } from '../../button/button.component';
import { ZardButtonGroupComponent, ZardButtonGroupDividerComponent } from '../button-group.component';
@Component({
selector: 'z-demo-button-group-divider',
imports: [ZardButtonGroupComponent, ZardButtonComponent, ZardButtonGroupDividerComponent],
template: `
<z-button-group>
<button z-button zSize="sm" zType="secondary">Copy</button>
<z-button-group-divider />
<button z-button zSize="sm" zType="secondary">Paste</button>
</z-button-group>
`,
})
export class ZardDemoButtonGroupDividerComponent {}
import { Component } from '@angular/core';
import { ZardButtonComponent } from '../../button/button.component';
import { ZardIconComponent } from '../../icon/icon.component';
import { ZardInputDirective } from '../../input/input.directive';
import { ZardButtonGroupComponent } from '../button-group.component';
@Component({
selector: 'z-demo-button-group-input',
imports: [ZardButtonGroupComponent, ZardButtonComponent, ZardIconComponent, ZardInputDirective],
template: `
<z-button-group>
<input z-input placeholder="Search..." />
<button z-button zType="outline"><i z-icon zType="search"></i></button>
</z-button-group>
`,
})
export class ZardDemoButtonGroupInputComponent {}
import { Component, signal } from '@angular/core';
import { ZardInputDirective } from '../../input/input.directive';
import { ZardSelectItemComponent } from '../../select/select-item.component';
import { ZardSelectComponent } from '../../select/select.component';
import { ZardButtonGroupComponent } from '../button-group.component';
@Component({
selector: 'z-demo-button-group-select',
imports: [ZardButtonGroupComponent, ZardSelectComponent, ZardSelectItemComponent, ZardInputDirective],
template: `
<z-button-group>
<z-select [(zValue)]="currency" class="w-fit">
@for (cur of CURRENCIES; track cur.code) {
<z-select-item [zValue]="cur.code">{{ cur.symbol }}</z-select-item>
}
</z-select>
<input z-input placeholder="10.00" type="number" step="0.1" />
</z-button-group>
`,
})
export class ZardDemoButtonGroupSelectComponent {
protected readonly CURRENCIES = [
{ symbol: '€', code: 'EUR' },
{ symbol: '$', code: 'USD' },
{ symbol: '£', code: 'GBP' },
{ symbol: '¥', code: 'JPY' },
];
protected readonly currency = signal(this.CURRENCIES[1].code);
}