Run the CLI
Use the CLI to add the component to your project.
npx @ngzard/ui@latest add tableDisplays data in a structured table format with styling variants and semantic HTML structure.
| 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 { ZardTableComponent } from '../table.component';
interface Person {
key: string;
name: string;
age: number;
address: string;
}
@Component({
selector: 'z-demo-table-simple',
imports: [ZardTableComponent],
standalone: true,
template: `
<table z-table>
<caption>
A list of your recent invoices.
</caption>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
@for (data of listOfData; track data.key) {
<tr>
<td class="font-medium">{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ 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',
},
];
}
Use the CLI to add the component to your project.
npx @ngzard/ui@latest add tablepnpm dlx @ngzard/ui@latest add tableyarn dlx @ngzard/ui@latest add tablebunx @ngzard/ui@latest add tableCreate the component directory structure and add the following files to your project.
import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation } from '@angular/core';
import type { ClassValue } from 'clsx';
import {
tableVariants,
tableHeaderVariants,
tableBodyVariants,
tableRowVariants,
tableHeadVariants,
tableCellVariants,
tableCaptionVariants,
type ZardTableVariants,
} from './table.variants';
import { mergeClasses } from '../../shared/utils/utils';
@Component({
selector: 'table[z-table]',
standalone: true,
template: `<ng-content />`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class]': 'classes()',
},
exportAs: 'zTable',
})
export class ZardTableComponent {
readonly zType = input<ZardTableVariants['zType']>('default');
readonly zSize = input<ZardTableVariants['zSize']>('default');
readonly class = input<ClassValue>('');
protected readonly classes = computed(() =>
mergeClasses(
tableVariants({
zType: this.zType(),
zSize: this.zSize(),
}),
this.class(),
),
);
}
@Component({
selector: 'thead[z-table-header]',
standalone: true,
template: `<ng-content />`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class]': 'classes()',
},
exportAs: 'zTableHeader',
})
export class ZardTableHeaderComponent {
readonly class = input<ClassValue>('');
protected readonly classes = computed(() => mergeClasses(tableHeaderVariants(), this.class()));
}
@Component({
selector: 'tbody[z-table-body]',
standalone: true,
template: `<ng-content />`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class]': 'classes()',
},
exportAs: 'zTableBody',
})
export class ZardTableBodyComponent {
readonly class = input<ClassValue>('');
protected readonly classes = computed(() => mergeClasses(tableBodyVariants(), this.class()));
}
@Component({
selector: 'tr[z-table-row]',
standalone: true,
template: `<ng-content />`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class]': 'classes()',
},
exportAs: 'zTableRow',
})
export class ZardTableRowComponent {
readonly class = input<ClassValue>('');
protected readonly classes = computed(() => mergeClasses(tableRowVariants(), this.class()));
}
@Component({
selector: 'th[z-table-head]',
standalone: true,
template: `<ng-content />`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class]': 'classes()',
},
exportAs: 'zTableHead',
})
export class ZardTableHeadComponent {
readonly class = input<ClassValue>('');
protected readonly classes = computed(() => mergeClasses(tableHeadVariants(), this.class()));
}
@Component({
selector: 'td[z-table-cell]',
standalone: true,
template: `<ng-content />`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class]': 'classes()',
},
exportAs: 'zTableCell',
})
export class ZardTableCellComponent {
readonly class = input<ClassValue>('');
protected readonly classes = computed(() => mergeClasses(tableCellVariants(), this.class()));
}
@Component({
selector: 'caption[z-table-caption]',
standalone: true,
template: `<ng-content />`,
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
host: {
'[class]': 'classes()',
},
exportAs: 'zTableCaption',
})
export class ZardTableCaptionComponent {
readonly class = input<ClassValue>('');
protected readonly classes = computed(() => mergeClasses(tableCaptionVariants(), this.class()));
}
import { cva, type VariantProps } from 'class-variance-authority';
export const tableVariants = cva(
'w-full caption-bottom text-sm [&_thead_tr]:border-b [&_tbody]:border-0 [&_tbody_tr:last-child]:border-0 [&_tbody_tr]:border-b [&_tbody_tr]:transition-colors [&_tbody_tr]:hover:bg-muted/50 [&_tbody_tr]:data-[state=selected]:bg-muted [&_th]:h-10 [&_th]:px-2 [&_th]:text-left [&_th]:align-middle [&_th]:font-medium [&_th]:text-muted-foreground [&_th:has([role=checkbox])]:pr-0 [&_th>[role=checkbox]]:translate-y-[2px] [&_td]:p-2 [&_td]:align-middle [&_td:has([role=checkbox])]:pr-0 [&_td>[role=checkbox]]:translate-y-[2px] [&_caption]:mt-4 [&_caption]:text-sm [&_caption]:text-muted-foreground',
{
variants: {
zType: {
default: '',
striped: '[&_tbody_tr:nth-child(odd)]:bg-muted/50',
bordered: 'border border-border',
},
zSize: {
default: '',
compact: '[&_td]:py-2 [&_th]:py-2',
comfortable: '[&_td]:py-4 [&_th]:py-4',
},
},
defaultVariants: {
zType: 'default',
zSize: 'default',
},
},
);
export const tableHeaderVariants = cva('[&_tr]:border-b', {
variants: {},
defaultVariants: {},
});
export const tableBodyVariants = cva('[&_tr:last-child]:border-0', {
variants: {},
defaultVariants: {},
});
export const tableRowVariants = cva('border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted', {
variants: {},
defaultVariants: {},
});
export const tableHeadVariants = cva(
'h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
{
variants: {},
defaultVariants: {},
},
);
export const tableCellVariants = cva(
'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
{
variants: {},
defaultVariants: {},
},
);
export const tableCaptionVariants = cva('mt-4 text-sm text-muted-foreground', {
variants: {},
defaultVariants: {},
});
export type ZardTableVariants = VariantProps<typeof tableVariants>;
export type ZardTableHeaderVariants = VariantProps<typeof tableHeaderVariants>;
export type ZardTableBodyVariants = VariantProps<typeof tableBodyVariants>;
export type ZardTableRowVariants = VariantProps<typeof tableRowVariants>;
export type ZardTableHeadVariants = VariantProps<typeof tableHeadVariants>;
export type ZardTableCellVariants = VariantProps<typeof tableCellVariants>;
export type ZardTableCaptionVariants = VariantProps<typeof tableCaptionVariants>;
import { NgModule } from '@angular/core';
import {
ZardTableComponent,
ZardTableHeaderComponent,
ZardTableBodyComponent,
ZardTableRowComponent,
ZardTableHeadComponent,
ZardTableCellComponent,
ZardTableCaptionComponent,
} from './table.component';
const TABLE_COMPONENTS = [
ZardTableComponent,
ZardTableHeaderComponent,
ZardTableBodyComponent,
ZardTableRowComponent,
ZardTableHeadComponent,
ZardTableCellComponent,
ZardTableCaptionComponent,
];
@NgModule({
imports: [...TABLE_COMPONENTS],
exports: [...TABLE_COMPONENTS],
})
export class ZardTableModule {}
| 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 { ZardTableComponent } from '../table.component';
interface Person {
key: string;
name: string;
age: number;
address: string;
}
@Component({
selector: 'z-demo-table-simple',
imports: [ZardTableComponent],
standalone: true,
template: `
<table z-table>
<caption>
A list of your recent invoices.
</caption>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>Address</th>
</tr>
</thead>
<tbody>
@for (data of listOfData; track data.key) {
<tr>
<td class="font-medium">{{ data.name }}</td>
<td>{{ data.age }}</td>
<td>{{ 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',
},
];
}
| Status | Amount | Actions | |
|---|---|---|---|
ken99@example.com | $316.00 | ||
Abe45@example.com | $242.00 | ||
Monserrat44@example.com | $837.00 | ||
Silas22@example.com | $874.00 | ||
carmella@example.com | $721.00 | ||
jane.doe@example.com | $456.00 |
import { Component } from '@angular/core';
import { ZardBadgeComponent } from '../../badge/badge.component';
import { ZardButtonComponent } from '../../button/button.component';
import { ZardIconComponent } from '../../icon/icon.component';
import {
ZardTableBodyComponent,
ZardTableCellComponent,
ZardTableComponent,
ZardTableHeadComponent,
ZardTableHeaderComponent,
ZardTableRowComponent,
} from '../table.component';
export interface Payment {
id: string;
amount: number;
status: 'pending' | 'processing' | 'success' | 'failed';
email: string;
}
@Component({
selector: 'z-demo-table-payments',
imports: [
ZardTableComponent,
ZardTableHeaderComponent,
ZardTableBodyComponent,
ZardTableRowComponent,
ZardTableHeadComponent,
ZardTableCellComponent,
ZardBadgeComponent,
ZardButtonComponent,
ZardIconComponent,
],
standalone: true,
template: `
<div class="w-full">
<div class="overflow-hidden rounded-md border">
<table z-table>
<thead z-table-header>
<tr z-table-row>
<th z-table-head>Status</th>
<th z-table-head>Email</th>
<th z-table-head class="text-right">Amount</th>
<th z-table-head class="w-16">Actions</th>
</tr>
</thead>
<tbody z-table-body>
@for (payment of payments; track payment.id) {
<tr z-table-row>
<td z-table-cell>
<z-badge [zType]="getStatusVariant(payment.status)">
{{ payment.status }}
</z-badge>
</td>
<td z-table-cell>
<div class="lowercase">{{ payment.email }}</div>
</td>
<td z-table-cell>
<div class="text-right font-medium">{{ formatCurrency(payment.amount) }}</div>
</td>
<td z-table-cell>
<div class="flex items-center gap-2">
<z-button zType="ghost" (click)="copyPaymentId(payment.id)" title="Copy payment ID">
<div z-icon zType="copy"></div>
</z-button>
<z-button zType="ghost" (click)="viewDetails(payment)" title="View details">
<div z-icon zType="eye"></div>
</z-button>
</div>
</td>
</tr>
} @empty {
<tr z-table-row>
<td z-table-cell [attr.colspan]="4" class="h-24 text-center">No results.</td>
</tr>
}
</tbody>
</table>
</div>
</div>
`,
})
export class ZardDemoTablePaymentsComponent {
payments: Payment[] = [
{
id: 'm5gr84i9',
amount: 316,
status: 'success',
email: 'ken99@example.com',
},
{
id: '3u1reuv4',
amount: 242,
status: 'success',
email: 'Abe45@example.com',
},
{
id: 'derv1ws0',
amount: 837,
status: 'processing',
email: 'Monserrat44@example.com',
},
{
id: '5kma53ae',
amount: 874,
status: 'success',
email: 'Silas22@example.com',
},
{
id: 'bhqecj4p',
amount: 721,
status: 'failed',
email: 'carmella@example.com',
},
{
id: 'abc123ef',
amount: 456,
status: 'pending',
email: 'jane.doe@example.com',
},
];
formatCurrency(amount: number): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(amount);
}
getStatusVariant(status: Payment['status']): 'default' | 'secondary' | 'destructive' | 'outline' {
switch (status) {
case 'success':
return 'default';
case 'processing':
return 'secondary';
case 'failed':
return 'destructive';
case 'pending':
return 'outline';
default:
return 'secondary';
}
}
copyPaymentId(id: string): void {
navigator.clipboard.writeText(id);
console.log('Payment ID copied:', id);
}
viewDetails(payment: Payment): void {
console.log('View payment details:', payment);
}
}