Run the CLI
Use the CLI to add input-otp to your project.
One-time passwords land this month. We added the Input OTP component, with grouped slots, separators, pattern validation, and paste support out of the box.
Use the CLI to add input-otp to your project.
npx zard-cli@latest add input-otppnpm dlx zard-cli@latest add input-otpyarn dlx zard-cli@latest add input-otpbunx zard-cli@latest add input-otpimport { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ZardInputOtpImports } from '@/shared/components/input-otp/input-otp.imports';
@Component({
selector: 'z-demo-input-otp-preview',
imports: [ZardInputOtpImports, FormsModule],
template: `
<z-input-otp [zMaxLength]="6" [(ngModel)]="value">
<z-input-otp-group>
<z-input-otp-slot [zIndex]="0" />
<z-input-otp-slot [zIndex]="1" />
<z-input-otp-slot [zIndex]="2" />
<z-input-otp-slot [zIndex]="3" />
<z-input-otp-slot [zIndex]="4" />
<z-input-otp-slot [zIndex]="5" />
</z-input-otp-group>
</z-input-otp>
`,
})
export class ZardDemoInputOtpPreviewComponent {
value = '123456';
}
Four new components this month — Field, Item, Textarea, and Sonner — and a reorganisation that moved every demo and API doc next to the component it documents.
The `toast` component has been replaced by `sonner`. Swap `z-toast` / `z-toaster` for `z-sonner`, import `ZardSonnerService` instead of the toast service, and reinstall under the new name. The old component no longer ships.
npx zard-cli@latest add sonnerEvery demo, API reference, and overview moved from the documentation site into `libs/zard/.../components/<name>/`. Nothing changes for you as a consumer — the CLI and the registry are unaffected — but contributing a component is now a single folder.
Use the CLI to add field to your project.
npx zard-cli@latest add fieldpnpm dlx zard-cli@latest add fieldyarn dlx zard-cli@latest add fieldbunx zard-cli@latest add fieldimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardCheckboxComponent } from '@/shared/components/checkbox/checkbox.component';
import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardInputComponent } from '@/shared/components/input/input.component';
import { ZardSelectImports } from '@/shared/components/select/select.imports';
import { ZardTextareaComponent } from '@/shared/components/textarea/textarea.component';
@Component({
selector: 'z-demo-field-default',
imports: [
...ZardFieldImports,
ZardButtonComponent,
ZardCheckboxComponent,
ZardInputComponent,
ZardTextareaComponent,
ZardSelectImports,
FormsModule,
],
template: `
<div class="w-full min-w-md">
<form>
<div z-field-group>
<fieldset z-field-set>
<legend z-field-legend>Payment Method</legend>
<p z-field-description>All transactions are secure and encrypted.</p>
<div z-field-group>
<div z-field>
<label z-field-label for="checkout-card-name">Name on Card</label>
<input z-input id="checkout-card-name" placeholder="Evil Rabbit" zSize="sm" required />
</div>
<div z-field>
<label z-field-label for="checkout-card-number">Card Number</label>
<input z-input id="checkout-card-number" placeholder="1234 5678 9012 3456" zSize="sm" required />
<p z-field-description>Enter your 16-digit card number.</p>
</div>
<div class="grid grid-cols-3 gap-4">
<div z-field>
<label z-field-label for="checkout-exp-month" class="w-fit">Month</label>
<z-select id="checkout-exp-month" zPlaceholder="MM" zSize="sm">
<z-select-item zValue="01">01</z-select-item>
<z-select-item zValue="02">02</z-select-item>
<z-select-item zValue="03">03</z-select-item>
<z-select-item zValue="04">04</z-select-item>
<z-select-item zValue="05">05</z-select-item>
<z-select-item zValue="06">06</z-select-item>
<z-select-item zValue="07">07</z-select-item>
<z-select-item zValue="08">08</z-select-item>
<z-select-item zValue="09">09</z-select-item>
<z-select-item zValue="10">10</z-select-item>
<z-select-item zValue="11">11</z-select-item>
<z-select-item zValue="12">12</z-select-item>
</z-select>
</div>
<div z-field>
<label z-field-label for="checkout-exp-year">Year</label>
<z-select id="checkout-exp-year" zPlaceholder="YYYY" zSize="sm">
<z-select-item zValue="2024">2024</z-select-item>
<z-select-item zValue="2025">2025</z-select-item>
<z-select-item zValue="2026">2026</z-select-item>
<z-select-item zValue="2027">2027</z-select-item>
<z-select-item zValue="2028">2028</z-select-item>
<z-select-item zValue="2029">2029</z-select-item>
</z-select>
</div>
<div z-field>
<label z-field-label for="checkout-cvv">CVV</label>
<input z-input id="checkout-cvv" placeholder="123" zSize="sm" required />
</div>
</div>
</div>
</fieldset>
<z-field-separator />
<fieldset z-field-set>
<legend z-field-legend>Billing Address</legend>
<p z-field-description>The billing address associated with your payment method.</p>
<div z-field-group>
<div z-field zOrientation="horizontal">
<span
z-checkbox
zId="checkout-same-as-shipping"
[(ngModel)]="sameAsShipping"
name="sameAsShipping"
></span>
<label z-field-label for="checkout-same-as-shipping" class="font-normal">
Same as shipping address
</label>
</div>
</div>
</fieldset>
<fieldset z-field-set>
<div z-field-group>
<div z-field>
<label z-field-label for="checkout-comments">Comments</label>
<textarea
z-textarea
id="checkout-comments"
placeholder="Add any additional comments"
class="resize-none"
></textarea>
</div>
</div>
</fieldset>
<div z-field zOrientation="horizontal">
<button z-button type="submit">Submit</button>
<button z-button zType="outline" type="button">Cancel</button>
</div>
</div>
</form>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoFieldDefaultComponent {
protected sameAsShipping = true;
}
Use the CLI to add item to your project.
npx zard-cli@latest add itempnpm dlx zard-cli@latest add itemyarn dlx zard-cli@latest add itembunx zard-cli@latest add itemimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideBadgeCheck, lucideChevronRight } from '@ng-icons/lucide';
import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardItemImports } from '@/shared/components/item/item.imports';
@Component({
selector: 'z-demo-item-default',
imports: [ZardButtonComponent, NgIcon, ...ZardItemImports],
template: `
<div class="flex w-full min-w-md flex-col gap-6">
<z-item zVariant="outline">
<z-item-content>
<z-item-title>Basic Item</z-item-title>
<z-item-description>A simple item with title and description.</z-item-description>
</z-item-content>
<z-item-actions>
<button type="button" z-button zType="outline" zSize="sm">Action</button>
</z-item-actions>
</z-item>
<a z-item href="#" zVariant="outline" zSize="sm">
<z-item-media>
<ng-icon name="lucideBadgeCheck" size="1.25rem" />
</z-item-media>
<z-item-content>
<z-item-title>Your profile has been verified.</z-item-title>
</z-item-content>
<z-item-actions>
<ng-icon name="lucideChevronRight" size="1rem" />
</z-item-actions>
</a>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
viewProviders: [provideIcons({ lucideBadgeCheck, lucideChevronRight })],
})
export class ZardDemoItemDefaultComponent {}
Use the CLI to add textarea to your project.
npx zard-cli@latest add textareapnpm dlx zard-cli@latest add textareayarn dlx zard-cli@latest add textareabunx zard-cli@latest add textareaimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardTextareaComponent } from '@/shared/components/textarea/textarea.component';
@Component({
selector: 'z-demo-textarea-default',
imports: [ZardTextareaComponent],
template: `
<textarea z-textarea placeholder="Type your message here." class="w-72"></textarea>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoTextareaDefaultComponent {}
Use the CLI to add sonner to your project.
npx zard-cli@latest add sonnerpnpm dlx zard-cli@latest add sonneryarn dlx zard-cli@latest add sonnerbunx zard-cli@latest add sonnerimport { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardSonnerService } from '@/shared/components/sonner/sonner.service';
@Component({
selector: 'z-demo-sonner-default',
imports: [ZardButtonComponent],
template: `
<button type="button" z-button zType="outline" (click)="show()">Show Toast</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSonnerDefaultComponent {
private readonly sonner = inject(ZardSonnerService);
show() {
this.sonner.show('Event has been created', {
description: 'Sunday, December 03, 2023 at 9:00 AM',
action: {
label: 'Undo',
onClick: () => console.log('Undo'),
},
});
}
}
A quieter month focused on form correctness. We taught the input directive to speak numbers, and we changed one dialog default that had been surprising people.
The input directive now reads and writes real numbers for `type="number"` and `type="range"`, so a signal or reactive form typed as `number` stays a number instead of being turned into a string. An empty field resolves to `null`.
`zMaskClosable` now defaults to `false` on Alert Dialog. A confirmation is meant to be answered, not dismissed by a stray click. If you relied on the old behaviour, opt back in explicitly.
zMaskClosable: trueComplete icon system migration! We replaced the custom `ZardIconComponent` with `@ng-icons/lucide`, bringing better tree-shaking, a wider icon selection, and a simpler API across all components.
All components now use `NgIcon` from `@ng-icons/core` with icons sourced from `@ng-icons/lucide`. This replaces the deprecated `ZardIconComponent` and the custom icon registry, resulting in better tree-shaking and smaller bundle sizes.
Icons are now provided at component level using `provideIcons()` in `viewProviders` and rendered with `<ng-icon name="lucideIconName" />`. Import only the icons you need from `@ng-icons/lucide`.
The `lucide-angular` dependency has been replaced by `@ng-icons/core` and `@ng-icons/lucide`. The CLI `init` command now installs these automatically.
npm install @ng-icons/core @ng-icons/lucideMajor infrastructure improvements this month! We focused on developer experience with a new provider system, CLI enhancements with our own private registry, and streamlined dark-mode setup.
New provider function for streamlined app configuration. Add event manager plugins and other Zard utilities with a single function call in your app.config.ts.
provideZard()The CLI now uses our own private registry instead of fetching from GitHub. This brings significant performance improvements, better version control, and more reliable component installations.
Adding dark-mode support to your project is now as simple as running a single CLI command. It automatically configures everything you need.
npx zard-cli@latest add dark-modeMajor updates this month with new interactive components including Carousel, Button Group, Input Group, and Kbd components. Enhanced user experience with better form controls and keyboard navigation support.
Use the CLI to add carousel to your project.
npx zard-cli@latest add carouselpnpm dlx zard-cli@latest add carouselyarn dlx zard-cli@latest add carouselbunx zard-cli@latest add carouselimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardCardImports } from '@/shared/components/card/card.imports';
import { ZardCarouselImports } from '@/shared/components/carousel/carousel.imports';
@Component({
imports: [ZardCarouselImports, ZardCardImports],
template: `
<div class="w-full max-w-48 sm:max-w-xs">
<z-carousel>
<z-carousel-content>
@for (slide of slides; track slide) {
<z-carousel-item>
<div class="p-1">
<z-card>
<z-card-content class="flex aspect-square items-center justify-center p-6">
<span class="text-4xl font-semibold">{{ slide }}</span>
</z-card-content>
</z-card>
</div>
</z-carousel-item>
}
</z-carousel-content>
</z-carousel>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoCarouselPreviewComponent {
protected slides = ['1', '2', '3', '4', '5'];
}
Use the CLI to add kbd to your project.
npx zard-cli@latest add kbdpnpm dlx zard-cli@latest add kbdyarn dlx zard-cli@latest add kbdbunx zard-cli@latest add kbdimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardKbdGroupComponent } from '@/shared/components/kbd/kbd-group.component';
import { ZardKbdComponent } from '@/shared/components/kbd/kbd.component';
@Component({
selector: 'z-demo-kbd-default',
imports: [ZardKbdComponent, ZardKbdGroupComponent],
template: `
<div class="flex flex-col items-center justify-center gap-4">
<z-kbd-group>
<z-kbd>⌘</z-kbd>
<z-kbd>⇧</z-kbd>
<z-kbd>⌥</z-kbd>
<z-kbd>⌃</z-kbd>
</z-kbd-group>
<z-kbd-group>
<z-kbd>Ctrl</z-kbd>
<span>+</span>
<z-kbd>B</z-kbd>
</z-kbd-group>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoKbdDefaultComponent {}
Breaking changes with icons migration from lucide-static to lucide-angular for better performance. New layout components including Sheet and Empty state component for better UX.
Use the CLI to add sheet to your project.
npx zard-cli@latest add sheetpnpm dlx zard-cli@latest add sheetyarn dlx zard-cli@latest add sheetbunx zard-cli@latest add sheetimport { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ZardButtonComponent } from '@/shared/components/button';
import { ZardInputComponent } from '@/shared/components/input';
import { ZardSheetImports } from '@/shared/components/sheet/sheet.imports';
import { ZardSheetService } from '@/shared/components/sheet/sheet.service';
@Component({
selector: 'z-demo-sheet-preview-form',
imports: [FormsModule, ReactiveFormsModule, ZardInputComponent],
template: `
<form [formGroup]="form" class="grid flex-1 auto-rows-min gap-6 px-4">
<div class="grid gap-3">
<label for="sheet-demo-name" class="text-sm leading-none font-medium select-none">Name</label>
<input z-input id="sheet-demo-name" formControlName="name" />
</div>
<div class="grid gap-3">
<label for="sheet-demo-username" class="text-sm leading-none font-medium select-none">Username</label>
<input z-input id="sheet-demo-username" formControlName="username" />
</div>
</form>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'zardDemoSheetPreviewForm',
})
export class ZardDemoSheetPreviewFormComponent {
form = new FormGroup({
name: new FormControl('Pedro Duarte'),
username: new FormControl('@peduarte'),
});
}
@Component({
imports: [ZardButtonComponent, ZardSheetImports],
template: `
<button type="button" z-button zType="outline" (click)="openSheet()">Open</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSheetPreviewComponent {
private readonly sheetService = inject(ZardSheetService);
openSheet() {
this.sheetService.create({
zTitle: 'Edit profile',
zDescription: `Make changes to your profile here. Click save when you're done.`,
zContent: ZardDemoSheetPreviewFormComponent,
zOkText: 'Save changes',
zCancelText: 'Close',
zOnOk: instance => {
console.log('Form submitted:', instance.form.value);
},
});
}
}
Use the CLI to add empty to your project.
npx zard-cli@latest add emptypnpm dlx zard-cli@latest add emptyyarn dlx zard-cli@latest add emptybunx zard-cli@latest add emptyimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { lucideArrowUpRight, lucideFolderCode } from '@ng-icons/lucide';
import { ZardButtonComponent } from '@/shared/components/button';
import { ZardEmptyComponent } from '@/shared/components/empty';
@Component({
selector: 'z-demo-empty-preview',
imports: [ZardButtonComponent, ZardEmptyComponent, NgIcon],
template: `
<z-empty
zIcon="lucideFolderCode"
zTitle="No Projects Yet"
zDescription="You haven't created any projects yet. Get started by creating your first project."
[zActions]="[actionPrimary, actionSecondary]"
>
<ng-template #actionPrimary>
<button type="button" z-button>Create Project</button>
</ng-template>
<ng-template #actionSecondary>
<button type="button" z-button zType="outline">Import Project</button>
</ng-template>
<a z-button zType="link" zSize="sm" class="text-muted-foreground" href="#">
Learn More
<ng-icon name="lucideArrowUpRight" />
</a>
</z-empty>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
viewProviders: [
provideIcons({
lucideArrowUpRight,
lucideFolderCode,
}),
],
})
export class ZardDemoEmptyPreviewComponent {}
Focus on loading and feedback components this month. New Progress, Skeleton, and Loader components for better perceived performance and user feedback during async operations.
Use the CLI to add progress to your project.
npx zard-cli@latest add progresspnpm dlx zard-cli@latest add progressyarn dlx zard-cli@latest add progressbunx zard-cli@latest add progressimport { afterNextRender, ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { ZardProgressComponent } from '@/shared/components/progress/progress.component';
@Component({
selector: 'z-demo-progress-preview',
imports: [ZardProgressComponent],
template: `
<z-progress class="w-60" [value]="value()" />
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoProgressPreviewComponent {
protected readonly value = signal(13);
constructor() {
afterNextRender(() => {
setTimeout(() => this.value.set(66), 500);
});
}
}
Use the CLI to add skeleton to your project.
npx zard-cli@latest add skeletonpnpm dlx zard-cli@latest add skeletonyarn dlx zard-cli@latest add skeletonbunx zard-cli@latest add skeletonimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardSkeletonComponent } from '../skeleton.component';
@Component({
selector: 'z-demo-skeleton-default',
imports: [ZardSkeletonComponent],
template: `
<div class="flex items-center space-x-4">
<z-skeleton class="size-12 rounded-full" />
<div class="space-y-2">
<z-skeleton class="h-4 w-[250px]" />
<z-skeleton class="h-4 w-[200px]" />
</div>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSkeletonDefaultComponent {}
Use the CLI to add spinner to your project.
npx zard-cli@latest add spinnerpnpm dlx zard-cli@latest add spinneryarn dlx zard-cli@latest add spinnerbunx zard-cli@latest add spinnerimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardSpinnerComponent } from '@/shared/components/spinner/spinner.component';
@Component({
selector: 'z-demo-spinner-customization',
imports: [ZardSpinnerComponent],
template: `
<div class="flex items-center gap-4">
<z-spinner [zIcon]="customIcon" />
</div>
<ng-template #customIcon let-classes>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
[class]="classes"
>
<path d="M12 2v4" />
<path d="m16.2 7.8 2.9-2.9" />
<path d="M18 12h4" />
<path d="m16.2 16.2 2.9 2.9" />
<path d="M12 18v4" />
<path d="m4.9 19.1 2.9-2.9" />
<path d="M2 12h4" />
<path d="m4.9 4.9 2.9 2.9" />
</svg>
</ng-template>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSpinnerCustomizationComponent {}
Enhanced navigation and display components. New Avatar component with fallback support, Divider for content separation, and Breadcrumb for hierarchical navigation.
Use the CLI to add avatar to your project.
npx zard-cli@latest add avatarpnpm dlx zard-cli@latest add avataryarn dlx zard-cli@latest add avatarbunx zard-cli@latest add avatarimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardAvatarComponent } from '@/shared/components/avatar/avatar.component';
@Component({
selector: 'z-demo-avatar-basic',
imports: [ZardAvatarComponent],
template: `
<div class="mb-4 flex gap-3">
<z-avatar zSrc="/images/avatar/imgs/avatar_image.jpg" zFallback="ZA" />
<z-avatar zSrc="error-image.png" zFallback="ZA" />
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoAvatarBasicComponent {}
Use the CLI to add separator to your project.
npx zard-cli@latest add separatorpnpm dlx zard-cli@latest add separatoryarn dlx zard-cli@latest add separatorbunx zard-cli@latest add separatorimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardSeparatorComponent } from '../separator.component';
@Component({
selector: 'z-demo-separator-preview',
imports: [ZardSeparatorComponent],
template: `
<div class="flex max-w-sm flex-col gap-4 text-sm">
<div class="flex flex-col gap-1.5">
<div class="leading-none font-medium">Zard/ui</div>
<div class="text-muted-foreground">The Next Level for Your Angular Projects</div>
</div>
<z-separator />
<div>A set of beautifully designed components that you can customize, extend, and build on.</div>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSeparatorPreviewComponent {}
Use the CLI to add breadcrumb to your project.
npx zard-cli@latest add breadcrumbpnpm dlx zard-cli@latest add breadcrumbyarn dlx zard-cli@latest add breadcrumbbunx zard-cli@latest add breadcrumbimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardBreadcrumbImports } from '../breadcrumb.imports';
@Component({
selector: 'z-demo-breadcrumb-default',
imports: [ZardBreadcrumbImports],
template: `
<z-breadcrumb zLabel="Default breadcrumb">
<z-breadcrumb-item>
<a z-breadcrumb-link [routerLink]="['/']">Home</a>
</z-breadcrumb-item>
<z-breadcrumb-item>
<a z-breadcrumb-link [routerLink]="['/docs/components']">Components</a>
</z-breadcrumb-item>
<z-breadcrumb-item>
<span z-breadcrumb-page>Breadcrumb</span>
</z-breadcrumb-item>
</z-breadcrumb>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoBreadcrumbDefaultComponent {}
Major release of navigation and content organization components. New Tabs for multi-view interfaces, Accordion for collapsible content, and Tooltip for contextual information. Theming system improvements with new color palettes.
Use the CLI to add tabs to your project.
npx zard-cli@latest add tabspnpm dlx zard-cli@latest add tabsyarn dlx zard-cli@latest add tabsbunx zard-cli@latest add tabsYou have 12 active projects and 3 pending tasks.
Page views are up 25% compared to last month.
You have 5 reports ready and available to export.
Configure notifications, security, and themes.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardCardImports } from '@/shared/components/card/card.imports';
import { ZardTabComponent, ZardTabGroupComponent } from '../tabs.component';
@Component({
selector: 'z-demo-tabs-default',
imports: [ZardTabComponent, ZardTabGroupComponent, ZardCardImports],
template: `
<z-tab-group class="w-[400px]">
<z-tab label="Overview">
<z-card>
<z-card-header>
<z-card-title zTitle="Overview" />
<z-card-description
zDescription="View your key metrics and recent project activity. Track progress across all your active projects."
/>
</z-card-header>
<p z-card-content class="text-muted-foreground text-sm">You have 12 active projects and 3 pending tasks.</p>
</z-card>
</z-tab>
<z-tab label="Analytics">
<z-card>
<z-card-header>
<z-card-title zTitle="Analytics" />
<z-card-description
zDescription="Track performance and user engagement metrics. Monitor trends and identify growth opportunities."
/>
</z-card-header>
<p z-card-content class="text-muted-foreground text-sm">Page views are up 25% compared to last month.</p>
</z-card>
</z-tab>
<z-tab label="Reports">
<z-card>
<z-card-header>
<z-card-title zTitle="Reports" />
<z-card-description
zDescription="Generate and download your detailed reports. Export data in multiple formats for analysis."
/>
</z-card-header>
<p z-card-content class="text-muted-foreground text-sm">You have 5 reports ready and available to export.</p>
</z-card>
</z-tab>
<z-tab label="Settings">
<z-card>
<z-card-header>
<z-card-title zTitle="Settings" />
<z-card-description
zDescription="Manage your account preferences and options. Customize your experience to fit your needs."
/>
</z-card-header>
<p z-card-content class="text-muted-foreground text-sm">Configure notifications, security, and themes.</p>
</z-card>
</z-tab>
</z-tab-group>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoTabsDefaultComponent {}
Use the CLI to add accordion to your project.
npx zard-cli@latest add accordionpnpm dlx zard-cli@latest add accordionyarn dlx zard-cli@latest add accordionbunx zard-cli@latest add accordionimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardAccordionImports } from '@/shared/components/accordion/accordion.imports';
@Component({
selector: 'z-demo-accordion-basic',
imports: [ZardAccordionImports],
template: `
<div z-accordion zDefaultValue="item-1" zType="single" class="max-w-sm">
<z-accordion-item zValue="item-1" zTitle="How do I reset my password?">
Click on 'Forgot Password' on the login page, enter your email address, and we'll send you a link to reset your
password. The link will expire in 24 hours.
</z-accordion-item>
<z-accordion-item zValue="item-2" zTitle="Can I change my subscription plan?">
Yes, you can upgrade or downgrade your plan at any time from your account settings. Changes will be reflected in
your next billing cycle.
</z-accordion-item>
<z-accordion-item zValue="item-3" zTitle="What payment methods do you accept?">
We accept all major credit cards, PayPal, and bank transfers. All payments are processed securely through our
payment partners.
</z-accordion-item>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoAccordionBasicComponent {}
Use the CLI to add tooltip to your project.
npx zard-cli@latest add tooltippnpm dlx zard-cli@latest add tooltipyarn dlx zard-cli@latest add tooltipbunx zard-cli@latest add tooltipimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardButtonComponent } from '@/shared/components/button';
import { ZardTooltipImports } from '@/shared/components/tooltip/tooltip.imports';
@Component({
selector: 'z-demo-tooltip-hover',
imports: [ZardButtonComponent, ZardTooltipImports],
template: `
<button type="button" z-button zType="outline" zTooltip="Tooltip content">Hover</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoTooltipHoverComponent {}
Overlay components release! New Dialog, Popover, Alert Dialog, and Dropdown Menu components. CVA integration for type-safe styling variants across all components.
Use the CLI to add dialog to your project.
npx zard-cli@latest add dialogpnpm dlx zard-cli@latest add dialogyarn dlx zard-cli@latest add dialogbunx zard-cli@latest add dialogimport { ChangeDetectionStrategy, Component, inject, type AfterViewInit } from '@angular/core';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { ZardDialogImports } from '@/shared/components/dialog/dialog.imports';
import { ZardInputComponent } from '../../input/input.component';
import { Z_MODAL_DATA, ZardDialogService } from '../dialog.service';
interface iDialogData {
name: string;
username: string;
}
@Component({
selector: 'z-demo-dialog-basic',
imports: [FormsModule, ReactiveFormsModule, ZardInputComponent],
template: `
<form [formGroup]="form" class="grid gap-4">
<div class="grid gap-3">
<label for="name" class="text-sm leading-none font-medium select-none">Name</label>
<input z-input id="name" formControlName="name" />
</div>
<div class="grid gap-3">
<label for="username" class="text-sm leading-none font-medium select-none">Username</label>
<input z-input id="username" formControlName="username" />
</div>
</form>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
exportAs: 'zardDemoDialogBasic',
})
export class ZardDemoDialogBasicInputComponent implements AfterViewInit {
private zData = inject(Z_MODAL_DATA) as iDialogData;
form = new FormGroup({
name: new FormControl('Pedro Duarte'),
username: new FormControl('@peduarte'),
});
ngAfterViewInit(): void {
if (this.zData) {
this.form.patchValue(this.zData);
}
}
}
@Component({
imports: [ZardDialogImports],
template: `
<button type="button" z-button zType="outline" (click)="openDialog()">Edit profile</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoDialogBasicComponent {
private dialogService = inject(ZardDialogService);
openDialog() {
this.dialogService.create({
zTitle: 'Edit Profile',
zDescription: `Make changes to your profile here. Click save when you're done.`,
zContent: ZardDemoDialogBasicInputComponent,
zData: {
name: 'Samuel Rizzon',
username: '@samuelrizzondev',
} as iDialogData,
zOkText: 'Save changes',
zOnOk: instance => {
console.log('Form submitted:', instance.form.value);
},
});
}
}
Use the CLI to add popover to your project.
npx zard-cli@latest add popoverpnpm dlx zard-cli@latest add popoveryarn dlx zard-cli@latest add popoverbunx zard-cli@latest add popoverimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardInputComponent } from '@/shared/components/input/input.component';
import { ZardPopoverImports } from '@/shared/components/popover/popover.imports';
@Component({
selector: 'z-demo-popover-preview',
imports: [ZardButtonComponent, ZardInputComponent, ...ZardPopoverImports],
template: `
<button type="button" z-button zPopover zType="outline" [zContent]="popoverContent">Open popover</button>
<ng-template #popoverContent>
<z-popover class="w-80">
<div z-popover-header>
<h4 z-popover-title>Dimensions</h4>
<p z-popover-description>Set the dimensions for the layer.</p>
</div>
<div class="grid gap-2">
@for (dimension of dimensions; track dimension.id) {
<div class="grid grid-cols-3 items-center gap-4">
<label class="text-sm" [attr.for]="dimension.id">{{ dimension.label }}</label>
<input
z-input
type="text"
class="col-span-2 h-8"
[id]="dimension.id"
[value]="dimension.value"
[attr.aria-label]="dimension.label"
/>
</div>
}
</div>
</z-popover>
</ng-template>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoPopoverPreviewComponent {
readonly dimensions = [
{ id: 'width', label: 'Width', value: '100%' },
{ id: 'maxWidth', label: 'Max. width', value: '300px' },
{ id: 'height', label: 'Height', value: '25px' },
{ id: 'maxHeight', label: 'Max. height', value: 'none' },
];
}
Use the CLI to add alert-dialog to your project.
npx zard-cli@latest add alert-dialogpnpm dlx zard-cli@latest add alert-dialogyarn dlx zard-cli@latest add alert-dialogbunx zard-cli@latest add alert-dialogimport { ChangeDetectionStrategy, Component, inject } from '@angular/core';
import { ZardAlertDialogService } from '@/shared/components/alert-dialog/alert-dialog.service';
import { ZardButtonComponent } from '@/shared/components/button/button.component';
@Component({
selector: 'z-demo-alert-dialog-default',
imports: [ZardButtonComponent],
template: `
<button type="button" z-button zType="outline" (click)="open()">Show Dialog</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoAlertDialogDefaultComponent {
private readonly alertDialogService = inject(ZardAlertDialogService);
open() {
this.alertDialogService.create({
zTitle: 'Are you absolutely sure?',
zDescription: 'This action cannot be undone. This will permanently delete your account from our servers.',
zOkText: 'Continue',
zCancelText: 'Cancel',
});
}
}
Use the CLI to add dropdown to your project.
npx zard-cli@latest add dropdownpnpm dlx zard-cli@latest add dropdownyarn dlx zard-cli@latest add dropdownbunx zard-cli@latest add dropdownimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardButtonComponent } from '@/shared/components/button';
import { ZardDropdownImports } from '@/shared/components/dropdown/dropdown.imports';
@Component({
selector: 'z-demo-dropdown-default',
imports: [ZardDropdownImports, ZardButtonComponent],
template: `
<button type="button" z-button zType="outline" z-dropdown [zDropdownMenu]="menu">Open menu</button>
<z-dropdown-menu-content #menu="zDropdownMenuContent" class="w-48">
<z-dropdown-menu-item (click)="log('Profile')">Profile</z-dropdown-menu-item>
<z-dropdown-menu-item (click)="log('Billing')">Billing</z-dropdown-menu-item>
<z-dropdown-menu-item (click)="log('Team')">Team</z-dropdown-menu-item>
<z-dropdown-menu-item [disabled]="true">Subscription</z-dropdown-menu-item>
</z-dropdown-menu-content>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoDropdownDefaultComponent {
log(item: string) {
console.log(`${item} clicked`);
}
}
Comprehensive form controls release! New Select, Checkbox, Radio, Switch, and Slider components with full Angular Reactive Forms integration and ControlValueAccessor support.
Use the CLI to add select to your project.
npx zard-cli@latest add selectpnpm dlx zard-cli@latest add selectyarn dlx zard-cli@latest add selectbunx zard-cli@latest add selectimport { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { ZardSelectImports } from '@/shared/components/select/select.imports';
@Component({
selector: 'z-demo-select-default',
imports: [ZardSelectImports],
template: `
<z-select class="w-full min-w-48" zPlaceholder="Select a fruit" [(zValue)]="selectedFruit">
<z-select-label>Fruits</z-select-label>
<z-select-item zValue="apple">Apple</z-select-item>
<z-select-item zValue="banana">Banana</z-select-item>
<z-select-item zValue="blueberry">Blueberry</z-select-item>
<z-select-item zValue="grapes">Grapes</z-select-item>
<z-select-item zValue="pineapple">Pineapple</z-select-item>
</z-select>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSelectDefaultComponent {
readonly selectedFruit = signal('');
}
Use the CLI to add checkbox to your project.
npx zard-cli@latest add checkboxpnpm dlx zard-cli@latest add checkboxyarn dlx zard-cli@latest add checkboxbunx zard-cli@latest add checkboxBy clicking this checkbox, you agree to the terms.
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ZardCheckboxComponent } from '@/shared/components/checkbox/checkbox.component';
import { ZardFieldImports } from '@/shared/components/field/field.imports';
@Component({
selector: 'z-demo-checkbox-default',
imports: [ZardCheckboxComponent, ...ZardFieldImports, FormsModule],
template: `
<div z-field-group class="min-w-sm">
<div z-field zOrientation="horizontal">
<z-checkbox zId="terms-checkbox" [(ngModel)]="terms" />
<label z-field-label for="terms-checkbox">Accept terms and conditions</label>
</div>
<div z-field zOrientation="horizontal">
<z-checkbox zId="terms-checkbox-2" [(ngModel)]="termsWithDesc" />
<div z-field-content>
<label z-field-label for="terms-checkbox-2">Accept terms and conditions</label>
<p z-field-description>By clicking this checkbox, you agree to the terms.</p>
</div>
</div>
<div z-field zOrientation="horizontal" data-disabled="true">
<z-checkbox zId="toggle-checkbox" zDisabled />
<label z-field-label for="toggle-checkbox">Enable notifications</label>
</div>
<label z-field-label>
<div z-field zOrientation="horizontal">
<z-checkbox zId="toggle-checkbox-2" [(ngModel)]="notifications" />
<div z-field-content>
<div z-field-title>Enable notifications</div>
<p z-field-description>You can enable or disable notifications at any time.</p>
</div>
</div>
</label>
</div>
`,
})
export class ZardDemoCheckboxDefaultComponent {
terms = false;
termsWithDesc = true;
notifications = false;
}
Use the CLI to add radio-group to your project.
npx zard-cli@latest add radio-grouppnpm dlx zard-cli@latest add radio-groupyarn dlx zard-cli@latest add radio-groupbunx zard-cli@latest add radio-groupimport { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardRadioGroupImports } from '@/shared/components/radio-group/radio-group.imports';
@Component({
selector: 'z-demo-radio-group-default',
imports: [...ZardRadioGroupImports, ...ZardFieldImports, FormsModule],
template: `
<z-radio-group [(value)]="selected" class="w-fit">
<div class="flex items-center gap-3">
<z-radio zId="r1" value="default" />
<label z-field-label for="r1">Default</label>
</div>
<div class="flex items-center gap-3">
<z-radio zId="r2" value="comfortable" />
<label z-field-label for="r2">Comfortable</label>
</div>
<div class="flex items-center gap-3">
<z-radio zId="r3" value="compact" />
<label z-field-label for="r3">Compact</label>
</div>
</z-radio-group>
`,
})
export class ZardDemoRadioGroupDefaultComponent {
selected: unknown = 'comfortable';
}
Use the CLI to add switch to your project.
npx zard-cli@latest add switchpnpm dlx zard-cli@latest add switchyarn dlx zard-cli@latest add switchbunx zard-cli@latest add switchimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardSwitchComponent } from '@/shared/components/switch/switch.component';
@Component({
selector: 'z-demo-switch',
imports: [ZardSwitchComponent],
template: `
<z-switch zId="airplane-mode">Airplane Mode</z-switch>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSwitchDefaultComponent {}
Use the CLI to add slider to your project.
npx zard-cli@latest add sliderpnpm dlx zard-cli@latest add slideryarn dlx zard-cli@latest add sliderbunx zard-cli@latest add sliderimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardSliderComponent } from '../slider.component';
@Component({
selector: 'z-demo-slider-default',
imports: [ZardSliderComponent],
template: `
<div class="flex min-h-50 w-full items-center p-10">
<z-slider class="mx-auto w-full max-w-xs" [zDefault]="[75]" />
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoSliderDefaultComponent {}
Form foundations and CLI launch! New Input and Form components with validation support. Official CLI tool released for easy project initialization and component installation. The Form component has since been discontinued — use Field instead, which covers the same ground and is what the form guides are built on.
Use the CLI to add input to your project.
npx zard-cli@latest add inputpnpm dlx zard-cli@latest add inputyarn dlx zard-cli@latest add inputbunx zard-cli@latest add inputYour API key is encrypted and stored securely.
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardFieldImports } from '@/shared/components/field/field.imports';
import { ZardInputComponent } from '@/shared/components/input/input.component';
@Component({
selector: 'z-demo-input-default',
imports: [ZardInputComponent, ...ZardFieldImports],
template: `
<div z-field class="w-72">
<label z-field-label for="input-demo-api-key">API Key</label>
<input z-input id="input-demo-api-key" type="password" placeholder="sk-..." />
<p z-field-description>Your API key is encrypted and stored securely.</p>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoInputDefaultComponent {}
🎉 Initial release of ZardUI! An Angular component library built with TailwindCSS v4, featuring standalone components, signal-based reactivity, and modern Angular architecture. Core components including Button, Card, Badge, Alert, and Table.
Use the CLI to add button to your project.
npx zard-cli@latest add buttonpnpm dlx zard-cli@latest add buttonyarn dlx zard-cli@latest add buttonbunx zard-cli@latest add buttonimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardButtonComponent } from '../button.component';
@Component({
selector: 'z-demo-button-default',
imports: [ZardButtonComponent],
template: `
<button type="button" z-button>Button</button>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoButtonDefaultComponent {}
Use the CLI to add card to your project.
npx zard-cli@latest add cardpnpm dlx zard-cli@latest add cardyarn dlx zard-cli@latest add cardbunx zard-cli@latest add cardimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardButtonComponent } from '@/shared/components/button/button.component';
import { ZardCardImports } from '@/shared/components/card/card.imports';
import { ZardIdDirective } from '@/shared/core';
@Component({
selector: 'z-demo-card-default',
imports: [ZardCardImports, ZardButtonComponent, ZardIdDirective],
template: `
<z-card class="w-full md:w-94">
<div z-card-header>
<z-card-title zTitle="Login to your account" />
<z-card-description zDescription="Enter your email below to login to your account" />
<z-card-action>
<button z-button type="button" zType="link" (click)="onSignUp()">Sign up</button>
</z-card-action>
</div>
<div z-card-content>
<div class="space-y-4">
<div class="space-y-2" zardId="email" #e="zardId">
<label
[for]="e.id()"
class="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Email
</label>
<input
[id]="e.id()"
type="email"
placeholder="m@example.com"
class="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
required
/>
</div>
<div class="space-y-2">
<div class="flex items-center" zardId="password" #p="zardId">
<label
[for]="p.id()"
class="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Password
</label>
<a href="#" class="ml-auto text-sm underline-offset-4 hover:underline">Forgot your password?</a>
</div>
<input
[id]="p.id()"
type="password"
class="border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex h-10 w-full rounded-md border px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50"
required
/>
</div>
</div>
</div>
<div z-card-footer class="flex-col gap-2">
<z-button zType="default" class="w-full">Login</z-button>
<z-button zType="outline" class="w-full">Login with Google</z-button>
</div>
</z-card>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoCardDefaultComponent {
protected onSignUp(): void {
alert('Redirect to Sign Up');
}
}
Use the CLI to add badge to your project.
npx zard-cli@latest add badgepnpm dlx zard-cli@latest add badgeyarn dlx zard-cli@latest add badgebunx zard-cli@latest add badgeimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { ZardBadgeComponent } from '../badge.component';
@Component({
selector: 'z-demo-badge-default',
imports: [ZardBadgeComponent],
template: `
<div class="flex flex-col items-center gap-2">
<div class="flex w-full flex-wrap gap-2">
<z-badge>Badge</z-badge>
<z-badge zType="secondary">Secondary</z-badge>
<z-badge zType="destructive">Destructive</z-badge>
<z-badge zType="outline">Outline</z-badge>
<z-badge zType="ghost">Ghost</z-badge>
</div>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ZardDemoBadgeDefaultComponent {}
Use the CLI to add alert to your project.
npx zard-cli@latest add alertpnpm dlx zard-cli@latest add alertyarn dlx zard-cli@latest add alertbunx zard-cli@latest add alertimport { ChangeDetectionStrategy, Component } from '@angular/core';
import { provideIcons } from '@ng-icons/core';
import { lucideCircleCheck, lucideInfo } from '@ng-icons/lucide';
import { ZardAlertComponent } from '../alert.component';
@Component({
selector: 'z-demo-alert-basic',
imports: [ZardAlertComponent],
template: `
<div class="grid w-full max-w-md items-start gap-4">
<z-alert
zIcon="lucideCircleCheck"
zTitle="Payment successful"
zDescription="Your payment of $29.99 has been processed. A receipt has been sent to your email address."
/>
<z-alert
zIcon="lucideInfo"
zTitle="New feature available"
zDescription="We've added dark mode support. You can enable it in your account settings."
/>
</div>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
viewProviders: [provideIcons({ lucideCircleCheck, lucideInfo })],
})
export class ZardDemoAlertBasicComponent {}
Use the CLI to add table to your project.
npx zard-cli@latest add tablepnpm dlx zard-cli@latest add tableyarn dlx zard-cli@latest add tablebunx zard-cli@latest add table| Name | Age | Address |
|---|---|---|
| John Brown | 32 | New York No. 1 Lake Park |
| Jim Green | 42 | London No. 1 Lake Park |
| Joe Black | 32 | Sidney No. 1 Lake Park |
import { Component } from '@angular/core';
import { ZardTableImports } from '@/shared/components/table/table.imports';
interface Person {
key: string;
name: string;
age: number;
address: string;
}
@Component({
selector: 'z-demo-table-simple',
imports: [ZardTableImports],
template: `
<table z-table>
<caption z-table-caption>A list of your recent invoices.</caption>
<thead z-table-header>
<tr z-table-row>
<th z-table-head>Name</th>
<th z-table-head>Age</th>
<th z-table-head>Address</th>
</tr>
</thead>
<tbody z-table-body>
@for (data of listOfData; track data.key) {
<tr z-table-row>
<td z-table-cell class="font-medium">{{ data.name }}</td>
<td z-table-cell>{{ data.age }}</td>
<td z-table-cell>{{ data.address }}</td>
</tr>
}
</tbody>
</table>
`,
})
export class ZardDemoTableSimpleComponent {
listOfData: Person[] = [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
];
}