General
Table
Usage#
import { WdsTableComponent } from '@wds/angular/table';<table> in wds-table: the element styles it and scrolls it horizontally when it does not fit. Align and size cells with your own classes (e.g. text-end tabular-nums, w-24). For sorting, filtering, selection and pagination driven by data use Data Table.
Examples#
Default#
caption under the table, a header row in thead, and a total in tfoot. Rows highlight on hover.
| Invoice | Status | Method | Amount |
|---|---|---|---|
| INV001 | Paid | Credit card | $250.00 |
| INV002 | Pending | PayPal | $150.00 |
| INV003 | Unpaid | Bank transfer | $350.00 |
| INV004 | Paid | Credit card | $450.00 |
| Total | $1,200.00 | ||
import { Component } from '@angular/core';
import { WdsTableComponent } from '@wds/angular/table';
@Component({
selector: 'app-table-default-demo',
imports: [WdsTableComponent],
template: `
<wds-table class="w-full max-w-2xl">
<table>
<caption>A list of your recent invoices.</caption>
<thead>
<tr>
<th scope="col" class="w-24">Invoice</th>
<th scope="col">Status</th>
<th scope="col">Method</th>
<th scope="col" class="text-end">Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td class="font-medium">INV001</td>
<td>Paid</td>
<td>Credit card</td>
<td class="text-end tabular-nums">$250.00</td>
</tr>
<tr>
<td class="font-medium">INV002</td>
<td>Pending</td>
<td>PayPal</td>
<td class="text-end tabular-nums">$150.00</td>
</tr>
<tr>
<td class="font-medium">INV003</td>
<td>Unpaid</td>
<td>Bank transfer</td>
<td class="text-end tabular-nums">$350.00</td>
</tr>
<tr>
<td class="font-medium">INV004</td>
<td>Paid</td>
<td>Credit card</td>
<td class="text-end tabular-nums">$450.00</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="3">Total</td>
<td class="text-end tabular-nums">$1,200.00</td>
</tr>
</tfoot>
</table>
</wds-table>
`,
})
export class TableDefaultDemo {}Bordered and striped#
bordered draws a rounded border around the table and striped shades every other row. Row headers are <th scope="row">.
| Region | Orders | Revenue | Change |
|---|---|---|---|
| North America | 1,284 | $48,210 | +12.4% |
| Europe | 936 | $35,870 | +8.1% |
| Asia Pacific | 712 | $24,630 | −2.3% |
| Latin America | 318 | $9,940 | +4.7% |
import { Component } from '@angular/core';
import { WdsTableComponent } from '@wds/angular/table';
@Component({
selector: 'app-table-bordered-demo',
imports: [WdsTableComponent],
template: `
<wds-table bordered striped class="w-full max-w-2xl">
<table>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col" class="text-end">Orders</th>
<th scope="col" class="text-end">Revenue</th>
<th scope="col" class="text-end">Change</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row" class="font-medium">North America</th>
<td class="text-end tabular-nums">1,284</td>
<td class="text-end tabular-nums">$48,210</td>
<td class="text-end tabular-nums">+12.4%</td>
</tr>
<tr>
<th scope="row" class="font-medium">Europe</th>
<td class="text-end tabular-nums">936</td>
<td class="text-end tabular-nums">$35,870</td>
<td class="text-end tabular-nums">+8.1%</td>
</tr>
<tr>
<th scope="row" class="font-medium">Asia Pacific</th>
<td class="text-end tabular-nums">712</td>
<td class="text-end tabular-nums">$24,630</td>
<td class="text-end tabular-nums">−2.3%</td>
</tr>
<tr>
<th scope="row" class="font-medium">Latin America</th>
<td class="text-end tabular-nums">318</td>
<td class="text-end tabular-nums">$9,940</td>
<td class="text-end tabular-nums">+4.7%</td>
</tr>
</tbody>
</table>
</wds-table>
`,
})
export class TableBorderedDemo {}Small#
size="sm" gives shorter rows for dense data.
| Key | Value |
|---|---|
| region | eu-central-1 |
| runtime | node 22 |
| memory | 1024 MB |
| timeout | 30 s |
| replicas | 3 |
import { Component } from '@angular/core';
import { WdsTableComponent } from '@wds/angular/table';
@Component({
selector: 'app-table-size-demo',
imports: [WdsTableComponent],
template: `
<wds-table size="sm" bordered class="w-full max-w-md">
<table>
<thead>
<tr>
<th scope="col">Key</th>
<th scope="col">Value</th>
</tr>
</thead>
<tbody>
<tr>
<td class="font-mono text-xs">region</td>
<td>eu-central-1</td>
</tr>
<tr>
<td class="font-mono text-xs">runtime</td>
<td>node 22</td>
</tr>
<tr>
<td class="font-mono text-xs">memory</td>
<td>1024 MB</td>
</tr>
<tr>
<td class="font-mono text-xs">timeout</td>
<td>30 s</td>
</tr>
<tr>
<td class="font-mono text-xs">replicas</td>
<td>3</td>
</tr>
</tbody>
</table>
</wds-table>
`,
})
export class TableSizeDemo {}Sticky header#
sticky-header keeps the header row visible while the rows scroll. Give the table a maximum height (here max-h-72). A table that scrolls and has nothing to focus can be scrolled with the keyboard and is a region named by its caption.
| Date | Job | Service | Result | Duration |
|---|---|---|---|---|
| Mon, Sep 1 | Deploy | api | Success | 42 s |
| Mon, Sep 1 | Migration | db | Success | 3 min |
| Tue, Sep 2 | Deploy | web | Failed | 1 min |
| Tue, Sep 2 | Rollback | web | Success | 38 s |
| Wed, Sep 3 | Deploy | web | Success | 51 s |
| Thu, Sep 4 | Backup | db | Success | 12 min |
| Fri, Sep 5 | Deploy | api | Success | 40 s |
| Mon, Sep 8 | Deploy | worker | Success | 35 s |
| Tue, Sep 9 | Migration | db | Success | 2 min |
| Wed, Sep 10 | Deploy | api | Failed | 1 min |
| Wed, Sep 10 | Deploy | api | Success | 44 s |
| Thu, Sep 11 | Backup | db | Success | 11 min |
import { Component } from '@angular/core';
import { WdsTableComponent } from '@wds/angular/table';
@Component({
selector: 'app-table-sticky-header-demo',
imports: [WdsTableComponent],
template: `
<wds-table sticky-header bordered class="max-h-72 w-full max-w-2xl">
<table>
<caption>Pipeline runs in September</caption>
<thead>
<tr>
<th scope="col">Date</th>
<th scope="col">Job</th>
<th scope="col">Service</th>
<th scope="col">Result</th>
<th scope="col" class="text-end">Duration</th>
</tr>
</thead>
<tbody>
<tr>
<td class="text-muted-foreground">Mon, Sep 1</td>
<td>Deploy</td>
<td class="font-mono text-xs">api</td>
<td>Success</td>
<td class="text-end tabular-nums">42 s</td>
</tr>
<tr>
<td class="text-muted-foreground">Mon, Sep 1</td>
<td>Migration</td>
<td class="font-mono text-xs">db</td>
<td>Success</td>
<td class="text-end tabular-nums">3 min</td>
</tr>
<tr>
<td class="text-muted-foreground">Tue, Sep 2</td>
<td>Deploy</td>
<td class="font-mono text-xs">web</td>
<td>Failed</td>
<td class="text-end tabular-nums">1 min</td>
</tr>
<tr>
<td class="text-muted-foreground">Tue, Sep 2</td>
<td>Rollback</td>
<td class="font-mono text-xs">web</td>
<td>Success</td>
<td class="text-end tabular-nums">38 s</td>
</tr>
<tr>
<td class="text-muted-foreground">Wed, Sep 3</td>
<td>Deploy</td>
<td class="font-mono text-xs">web</td>
<td>Success</td>
<td class="text-end tabular-nums">51 s</td>
</tr>
<tr>
<td class="text-muted-foreground">Thu, Sep 4</td>
<td>Backup</td>
<td class="font-mono text-xs">db</td>
<td>Success</td>
<td class="text-end tabular-nums">12 min</td>
</tr>
<tr>
<td class="text-muted-foreground">Fri, Sep 5</td>
<td>Deploy</td>
<td class="font-mono text-xs">api</td>
<td>Success</td>
<td class="text-end tabular-nums">40 s</td>
</tr>
<tr>
<td class="text-muted-foreground">Mon, Sep 8</td>
<td>Deploy</td>
<td class="font-mono text-xs">worker</td>
<td>Success</td>
<td class="text-end tabular-nums">35 s</td>
</tr>
<tr>
<td class="text-muted-foreground">Tue, Sep 9</td>
<td>Migration</td>
<td class="font-mono text-xs">db</td>
<td>Success</td>
<td class="text-end tabular-nums">2 min</td>
</tr>
<tr>
<td class="text-muted-foreground">Wed, Sep 10</td>
<td>Deploy</td>
<td class="font-mono text-xs">api</td>
<td>Failed</td>
<td class="text-end tabular-nums">1 min</td>
</tr>
<tr>
<td class="text-muted-foreground">Wed, Sep 10</td>
<td>Deploy</td>
<td class="font-mono text-xs">api</td>
<td>Success</td>
<td class="text-end tabular-nums">44 s</td>
</tr>
<tr>
<td class="text-muted-foreground">Thu, Sep 11</td>
<td>Backup</td>
<td class="font-mono text-xs">db</td>
<td>Success</td>
<td class="text-end tabular-nums">11 min</td>
</tr>
</tbody>
</table>
</wds-table>
`,
})
export class TableStickyHeaderDemo {}Rich cells#
| Member | Role | Status | Last active | Actions |
|---|---|---|---|---|
|
Mira Stone
|
Owner | 2 min ago | ||
|
Sana Qureshi
|
Admin | 1 h ago | ||
|
Ivo Vidal
|
Member | — |
import { Component } from '@angular/core';
import { WdsTableComponent } from '@wds/angular/table';
import { WdsAvatarComponent } from '@wds/angular/avatar';
import { WdsBadgeComponent } from '@wds/angular/badge';
import { WdsButtonComponent } from '@wds/angular/button';
@Component({
selector: 'app-table-rich-cells-demo',
imports: [WdsTableComponent, WdsAvatarComponent, WdsBadgeComponent, WdsButtonComponent],
template: `
<wds-table class="w-full max-w-3xl">
<table>
<thead>
<tr>
<th scope="col">Member</th>
<th scope="col">Role</th>
<th scope="col">Status</th>
<th scope="col" class="text-end">Last active</th>
<th scope="col" class="w-10"><span class="sr-only">Actions</span></th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="flex items-center gap-3">
<wds-avatar alt="">MS</wds-avatar>
<div>
<div class="font-medium">Mira Stone</div>
<div class="text-muted-foreground">mira@example.com</div>
</div>
</div>
</td>
<td>Owner</td>
<td><wds-badge variant="secondary">Active</wds-badge></td>
<td class="text-end text-muted-foreground">2 min ago</td>
<td><wds-button variant="ghost" size="icon-sm" label="Actions for Mira Stone"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg></wds-button></td>
</tr>
<tr>
<td>
<div class="flex items-center gap-3">
<wds-avatar alt="">SQ</wds-avatar>
<div>
<div class="font-medium">Sana Qureshi</div>
<div class="text-muted-foreground">sana@example.com</div>
</div>
</div>
</td>
<td>Admin</td>
<td><wds-badge variant="secondary">Active</wds-badge></td>
<td class="text-end text-muted-foreground">1 h ago</td>
<td><wds-button variant="ghost" size="icon-sm" label="Actions for Sana Qureshi"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg></wds-button></td>
</tr>
<tr>
<td>
<div class="flex items-center gap-3">
<wds-avatar alt="">IV</wds-avatar>
<div>
<div class="font-medium">Ivo Vidal</div>
<div class="text-muted-foreground">ivo@example.com</div>
</div>
</div>
</td>
<td>Member</td>
<td><wds-badge variant="outline">Invited</wds-badge></td>
<td class="text-end text-muted-foreground">—</td>
<td><wds-button variant="ghost" size="icon-sm" label="Actions for Ivo Vidal"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /><circle cx="5" cy="12" r="1" /></svg></wds-button></td>
</tr>
</tbody>
</table>
</wds-table>
`,
})
export class TableRichCellsDemo {}Selected row#
aria-selected="true" (or data-selected) on a row gives it the selected background.
| Name | Owner | Size |
|---|---|---|
| Q3 report.pdf | Mira Stone | 2.4 MB |
| Roadmap.key | Sana Qureshi | 18.1 MB |
| Brand assets.zip | Ivo Vidal | 146 MB |
import { Component } from '@angular/core';
import { WdsTableComponent } from '@wds/angular/table';
@Component({
selector: 'app-table-selected-demo',
imports: [WdsTableComponent],
template: `
<wds-table class="w-full max-w-2xl">
<table aria-label="Files">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Owner</th>
<th scope="col" class="text-end">Size</th>
</tr>
</thead>
<tbody>
<tr>
<td class="font-medium">Q3 report.pdf</td>
<td>Mira Stone</td>
<td class="text-end tabular-nums">2.4 MB</td>
</tr>
<tr aria-selected="true">
<td class="font-medium">Roadmap.key</td>
<td>Sana Qureshi</td>
<td class="text-end tabular-nums">18.1 MB</td>
</tr>
<tr>
<td class="font-medium">Brand assets.zip</td>
<td>Ivo Vidal</td>
<td class="text-end tabular-nums">146 MB</td>
</tr>
</tbody>
</table>
</wds-table>
`,
})
export class TableSelectedDemo {}API References#
wds-table#
<table> (with caption, thead, tbody, tfoot) inside.
The element styles the rows and cells and scrolls the table horizontally when it does not fit. For sorting,
filtering, selection and pagination driven by data use wds-data-table.
Mark a selected row with aria-selected="true" (or data-selected). Align numbers with your own classes on
the cells, e.g. text-end tabular-nums.
Attributes
| Attribute | Type | Default | Description |
|---|---|---|---|
| size | WdsTableSize | 'default' | sm for dense tables. |
| striped | boolean | false | |
| bordered | boolean | false | |
| sticky-header.stickyHeader | boolean | false | max-h-80),
so it scrolls inside. |
| label | string | '' | <caption>. |
Slots
| Slot | Description |
|---|---|
| (default) | <table>. |
CSS parts (::part)
| Part | Description |
|---|---|
| container | bordered). |
Accessibility#
Screen reader#
<table>, so screen readers announce rows, columns and the header of each cell. Use <th scope="col"> for column headers and <th scope="row"> for row headers.
<caption> (or aria-label on the <table>). When the table scrolls and has no focusable content, its container is a region named by the caption (or label) and can be focused, so it can be scrolled with the keyboard.
label on wds-button), e.g. "Actions for Mira Stone".
Keyboard support#
| Key | Function |
|---|---|