Docs
Table

Table

A responsive table component.

Preview Code
Loading...
Loading...
import {
  UbTableBodyDirective,
  UbTableCaptionDirective,
  UbTableCellDirective,
  UbTableDirective,
  UbTableFooterDirective,
  UbTableHeadDirective,
  UbTableHeaderDirective,
  UbTableRowDirective,
} from '@/components/ui/table'

import { Component } from '@angular/core'

@Component({
  standalone: true,
  selector: 'table-demo-new-york',
  imports: [
    UbTableDirective,
    UbTableCaptionDirective,
    UbTableHeaderDirective,
    UbTableRowDirective,
    UbTableHeadDirective,
    UbTableBodyDirective,
    UbTableCellDirective,
    UbTableFooterDirective,
  ],
  template: `
    <table ubTable>
      <caption ubTableCaption>
        A list of your recent invoices.
      </caption>

      <thead ubTableHeader>
        <tr ubTableRow>
          <th ubTableHead class="w-[100px]">Invoice</th>
          <th ubTableHead>Status</th>
          <th ubTableHead>Method</th>
          <th ubTableHead class="text-right">Amount</th>
        </tr>
      </thead>

      <tbody ubTableBody>
        @for (invoice of invoices; track $index) {
          <tr ubTableRow>
            <td ubTableCell class="font-medium">
              {{ invoice.invoice }}
            </td>
            <td ubTableCell>
              {{ invoice.paymentStatus }}
            </td>
            <td ubTableCell>
              {{ invoice.paymentMethod }}
            </td>
            <td ubTableCell class="text-right">
              {{ invoice.totalAmount }}
            </td>
          </tr>
        }
      </tbody>

      <tfoot ubTableFooter>
        <tr ubTableRow>
          <td ubTableCell colspan="3">Total</td>
          <td ubTableCell class="text-right">$2,500.00</td>
        </tr>
      </tfoot>
    </table>
  `,
})
export class TableDemoNewYork {
  invoices = [
    {
      invoice: 'INV001',
      paymentStatus: 'Paid',
      totalAmount: '$250.00',
      paymentMethod: 'Credit Card',
    },
    {
      invoice: 'INV002',
      paymentStatus: 'Pending',
      totalAmount: '$150.00',
      paymentMethod: 'PayPal',
    },
    {
      invoice: 'INV003',
      paymentStatus: 'Unpaid',
      totalAmount: '$350.00',
      paymentMethod: 'Bank Transfer',
    },
    {
      invoice: 'INV004',
      paymentStatus: 'Paid',
      totalAmount: '$450.00',
      paymentMethod: 'Credit Card',
    },
    {
      invoice: 'INV005',
      paymentStatus: 'Paid',
      totalAmount: '$550.00',
      paymentMethod: 'PayPal',
    },
    {
      invoice: 'INV006',
      paymentStatus: 'Pending',
      totalAmount: '$200.00',
      paymentMethod: 'Bank Transfer',
    },
    {
      invoice: 'INV007',
      paymentStatus: 'Unpaid',
      totalAmount: '$300.00',
      paymentMethod: 'Credit Card',
    },
  ]
}

import {
  UbTableBodyDirective,
  UbTableCaptionDirective,
  UbTableCellDirective,
  UbTableDirective,
  UbTableFooterDirective,
  UbTableHeadDirective,
  UbTableHeaderDirective,
  UbTableRowDirective,
} from '@/components/ui/table'

import { Component } from '@angular/core'

@Component({
  standalone: true,
  selector: 'table-demo-default',
  imports: [
    UbTableDirective,
    UbTableCaptionDirective,
    UbTableHeaderDirective,
    UbTableRowDirective,
    UbTableHeadDirective,
    UbTableBodyDirective,
    UbTableCellDirective,
    UbTableFooterDirective,
  ],
  template: `
    <table ubTable>
      <caption ubTableCaption>
        A list of your recent invoices.
      </caption>

      <thead ubTableHeader>
        <tr ubTableRow>
          <th ubTableHead class="w-[100px]">Invoice</th>
          <th ubTableHead>Status</th>
          <th ubTableHead>Method</th>
          <th ubTableHead class="text-right">Amount</th>
        </tr>
      </thead>

      <tbody ubTableBody>
        @for (invoice of invoices; track $index) {
          <tr ubTableRow>
            <td ubTableCell class="font-medium">
              {{ invoice.invoice }}
            </td>
            <td ubTableCell>
              {{ invoice.paymentStatus }}
            </td>
            <td ubTableCell>
              {{ invoice.paymentMethod }}
            </td>
            <td ubTableCell class="text-right">
              {{ invoice.totalAmount }}
            </td>
          </tr>
        }
      </tbody>

      <tfoot ubTableFooter>
        <tr ubTableRow>
          <td ubTableCell colspan="3">Total</td>
          <td ubTableCell class="text-right">$2,500.00</td>
        </tr>
      </tfoot>
    </table>
  `,
})
export class TableDemoDefault {
  invoices = [
    {
      invoice: 'INV001',
      paymentStatus: 'Paid',
      totalAmount: '$250.00',
      paymentMethod: 'Credit Card',
    },
    {
      invoice: 'INV002',
      paymentStatus: 'Pending',
      totalAmount: '$150.00',
      paymentMethod: 'PayPal',
    },
    {
      invoice: 'INV003',
      paymentStatus: 'Unpaid',
      totalAmount: '$350.00',
      paymentMethod: 'Bank Transfer',
    },
    {
      invoice: 'INV004',
      paymentStatus: 'Paid',
      totalAmount: '$450.00',
      paymentMethod: 'Credit Card',
    },
    {
      invoice: 'INV005',
      paymentStatus: 'Paid',
      totalAmount: '$550.00',
      paymentMethod: 'PayPal',
    },
    {
      invoice: 'INV006',
      paymentStatus: 'Pending',
      totalAmount: '$200.00',
      paymentMethod: 'Bank Transfer',
    },
    {
      invoice: 'INV007',
      paymentStatus: 'Unpaid',
      totalAmount: '$300.00',
      paymentMethod: 'Credit Card',
    },
  ]
}

Installation

CLI Manual
npx shadcn-ng@latest add table

Copy and paste the following code into your project.

import { cn } from '@/lib/utils'

import { computed, Directive, input } from '@angular/core'

@Directive({
  selector: 'table[ubTable]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn('w-full caption-bottom text-sm', this.class()),
  )
}

@Directive({
  selector: 'thead[ubTableHeader]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableHeaderDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() => cn('[&_tr]:border-b', this.class()))
}

@Directive({
  selector: 'tbody[ubTableBody]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableBodyDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn('[&_tr:last-child]:border-0', this.class()),
  )
}

@Directive({
  selector: 'tfoot[ubTableFooter]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableFooterDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', this.class()),
  )
}

@Directive({
  selector: 'tr[ubTableRow]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableRowDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn(
      'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
      this.class(),
    ),
  )
}

@Directive({
  selector: 'th[ubTableHead]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableHeadDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn(
      'h-12 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0',
      this.class(),
    ),
  )
}

@Directive({
  selector: 'td[ubTableCell]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableCellDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn('p-4 align-middle [&:has([role=checkbox])]:pr-0', this.class()),
  )
}

@Directive({
  selector: 'caption[ubTableCaption]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableCaptionDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn('mt-4 text-sm text-muted-foreground', this.class()),
  )
}
import { cn } from '@/lib/utils'

import { computed, Directive, input } from '@angular/core'

@Directive({
  selector: 'table[ubTable]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn('w-full caption-bottom text-sm', this.class()),
  )
}

@Directive({
  selector: 'thead[ubTableHeader]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableHeaderDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() => cn('[&_tr]:border-b', this.class()))
}

@Directive({
  selector: 'tbody[ubTableBody]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableBodyDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn('[&_tr:last-child]:border-0', this.class()),
  )
}

@Directive({
  selector: 'tfoot[ubTableFooter]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableFooterDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn('border-t bg-muted/50 font-medium [&>tr]:last:border-b-0', this.class()),
  )
}

@Directive({
  selector: 'tr[ubTableRow]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableRowDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn(
      'border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted',
      this.class(),
    ),
  )
}

@Directive({
  selector: 'th[ubTableHead]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableHeadDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn(
      'h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
      this.class(),
    ),
  )
}

@Directive({
  selector: 'td[ubTableCell]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableCellDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn(
      'p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]',
      this.class(),
    ),
  )
}

@Directive({
  selector: 'caption[ubTableCaption]',
  standalone: true,
  host: {
    '[class]': 'computedClass()',
  },
})
export class UbTableCaptionDirective {
  readonly class = input<string>('')
  protected computedClass = computed(() =>
    cn('mt-4 text-sm text-muted-foreground', this.class()),
  )
}

Update the import paths to match your project setup.

Usage

import {
  UbTableDirective,
  UbTableCaptionDirective,
  UbTableHeaderDirective,
  UbTableRowDirective,
  UbTableHeadDirective,
  UbTableBodyDirective,
  UbTableCellDirective,
} from "@/components/ui/table.directive";
<table ubTable>
    <caption ubTableCaption>A list of your recent invoices.</caption>

    <thead ubTableHeader>
        <tr ubTableRow>
            <th ubTableHead class="w-[100px]">Invoice</th>
            <th ubTableHead>Status</th>
            <th ubTableHead>Method</th>
            <th ubTableHead class="text-right">Amount</th>
        </tr>
    </thead>

    <tbody ubTableBody>
        <tr ubTableRow>
            <td ubTableCell class="font-medium">INV001</td>
            <td ubTableCell>Paid</td>
            <td ubTableCell>Credit Card</td>
            <td ubTableCell class="text-right">$250.00</td>
        </tr>
    </tbody>
</table>