Table
The <smart-table> component renders a tabular data view with optional title, description, toolbar slot, columns + rows, optional checkbox column, custom per-cell/per-header templates, an empty state slot, and a bottom footer slot. It follows the Base + Standard + Wrapper pattern with an InjectionToken-based extension mechanism. The abstract TableBaseComponent defines the shared API — optional ITableOptions and cssClass (alias class). TableStandardComponent is a barebones placeholder concrete implementation. TableComponent is the public wrapper that renders TableStandardComponent by default and accepts a custom replacement via TABLE_STANDARD_COMPONENT_TOKEN. TablePresetComponent is the styled Tailwind variation you register on that token (see Preset).
Usage
<smart-table [options]="options" />
Components
TableComponent (<smart-table>)
Main wrapper component. Renders TableStandardComponent by default. When TABLE_STANDARD_COMPONENT_TOKEN is provided, renders the injected component via NgComponentOutlet.
TableStandardComponent (<smart-table-standard>)
Barebones placeholder concrete implementation. Renders a wrapper <div> containing an optional <h3 class="title">, optional <p class="description">, optional toolbar slot, and a <table> with <thead> (one <th> per column) and <tbody> (one <tr> per row, one <td> per column reading the value via row[col.key]). When withCheckboxes is set, an extra checkbox column is rendered before all data columns. Per-column cellTpl and headerTpl templates override the default text rendering. When rows is empty and emptyTpl is provided, the empty template is rendered as a single full-width row. A bottom footerTpl renders inside <div class="footer">. The external cssClass is applied to the root wrapper. It does not include any visual styling — it exists solely as the default structural placeholder until a custom implementation is registered through the token.
TablePresetComponent (<smart-table-preset>)
Styled drop-in replacement for TableStandardComponent. See Preset.
TableBaseComponent (abstract)
Abstract base directive for extending custom table implementations. Exposes options as an InputSignal<ITableOptions | undefined> and cssClass as an InputSignal<string> (with alias class).
API
Inputs
| Input | Type | Default | Description |
|---|---|---|---|
options | InputSignal<ITableOptions | undefined> | - | Optional configuration (title, description, columns, rows, layout flags, empty/footer slots) |
class | InputSignal<string> | '' | External CSS classes (alias for cssClass) |
ITableOptions
All properties are optional except ITableColumn.key. The default TableStandardComponent consumes every property; a section is rendered only when its template/string is provided. striped, stickyHeader, withBorder, align and sortable are layout hints: the standard placeholder ignores them visually (it only sets data-align), while TablePresetComponent honours every one of them (see Preset).
TABLE_STANDARD_COMPONENT_TOKEN
InjectionToken that allows replacing the default TableStandardComponent with a custom implementation. Provide a Type<TableBaseComponent> to override.
Preset
TablePresetComponent (<smart-table-preset>) extends TableBaseComponent, so it takes the same options input, and it renders everything the standard component renders in the Tailwind UI table look. Every class carries the smart: prefix and has an explicit smart:dark:* twin. The class recipes live in preset/preset-classes.util.ts, which is not exported from the barrel.
What it styles:
- Header (
data-role="header", only rendered when there is a title, description or toolbar). The title is an<h3>withtext-base font-semibold text-gray-900 dark:text-white. The description usestext-sm text-gray-500 dark:text-gray-400.toolbarTplsits on the right of the header onsm+screens. - Table.
min-w-full divide-y divide-gray-300 dark:divide-white/15. Header cells usetext-sm font-semibold text-gray-900 dark:text-white. Body cells usewhitespace-nowrap text-sm: the first data column is emphasised (font-medium text-gray-900 dark:text-white) and the rest are muted (text-gray-500 dark:text-gray-400). - Checkbox column (
withCheckboxes). Styled checkboxes, with select-all and an indeterminate state in the header. Selected rows are highlighted withbg-gray-50 dark:bg-gray-800/50. Selection is internal visual state; the table has no outputs. - Slots.
cellTplgets{ $implicit: row, column }.headerTplreplaces the label.emptyTplrenders in a centred row that spans every column (checkbox column included).footerTplrenders under the table. - External class. Merged onto the root
<div>next tow-full.
The options the standard component ignores, which the preset honours:
| Option | Preset rendering |
|---|---|
| default | Rows separated by divide-y divide-gray-200 dark:divide-white/10, no frame, outer columns flush with the edges |
striped | Row dividers replaced by zebra stripes, even:bg-gray-50 dark:even:bg-gray-800/50 |
withBorder | Rounded card (rounded-lg bg-white shadow-sm outline-1 outline-black/5, dark:bg-gray-900 dark:outline-white/10) with a bg-gray-50 dark:bg-gray-800/75 header, outer cells padded |
stickyHeader | Body scrolls inside max-h-96 overflow-y-auto; header cells are sticky top-0 z-10 bg-white/75 backdrop-blur-sm dark:bg-gray-900/75 |
column.align | text-left / text-center / text-right on both the header and the body cells |
column.sortable | Heading becomes a button with a chevron. Click sorts the rows client-side ascending, click again for descending; aria-sort is set on the <th>. Numbers sort numerically, text in natural order, empty values last. The active column's chevron gets a bg-gray-100 dark:bg-gray-800 chip |
The flags combine freely (for example striped + bordered + sortable + checkboxes).
Because TableComponent forwards inputs by canonical name through NgComponentOutlet, the preset declares override cssClass = input<string>('') (dropping the inherited class alias). Pass class on <smart-table> as usual, or bind [cssClass] when you use <smart-table-preset> directly.
Register it on the token to restyle every <smart-table>:
The preset adds no new ITableOptions fields; it only consumes the existing API.
Extending the base class
import {
ChangeDetectionStrategy,
Component,
computed,
input,
ViewEncapsulation,
} from '@angular/core';
import {
ITableOptions,
TABLE_STANDARD_COMPONENT_TOKEN,
TableBaseComponent,
TableComponent,
TableRow,
} from '@smartsoft001/angular';
@Component({
selector: 'docs-custom-table',
template: `
<div [class]="containerClasses()">
@if (options()?.title) {
<h3 class="docs-table__title">{{ options()!.title }}</h3>
}
@if (options()?.description) {
<p class="docs-table__description">{{ options()!.description }}</p>
}
<table>
<thead>
<tr>
@for (col of options()?.columns ?? []; track col.key) {
<th scope="col" [attr.data-align]="col.align ?? null">
{{ col.label ?? col.key }}
</th>
}
</tr>
</thead>
<tbody>
@for (row of options()?.rows ?? []; track $index) {
<tr>
@for (col of options()?.columns ?? []; track col.key) {
<td [attr.data-align]="col.align ?? null">
{{ readCell(row, col.key) }}
</td>
}
</tr>
}
</tbody>
</table>
</div>
`,
encapsulation: ViewEncapsulation.None,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CustomTableComponent extends TableBaseComponent {
// NgComponentOutlet passes 'cssClass' by canonical name, not the 'class' alias.
override cssClass = input<string>('');
containerClasses = computed(() =>
[
'docs-table',
this.options()?.striped ? 'docs-table--striped' : '',
this.options()?.withBorder ? 'docs-table--bordered' : '',
this.cssClass(),
]
.filter(Boolean)
.join(' '),
);
// Rows are plain records, so cells are read by the column key.
protected readCell(row: TableRow, key: string): unknown {
return row[key];
}
}
@Component({
selector: 'docs-table-custom-example',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TableComponent],
// The token swaps the standard table for the custom one everywhere below this
// component, so consumers keep writing `<smart-table>`.
providers: [
{
provide: TABLE_STANDARD_COMPONENT_TOKEN,
useValue: CustomTableComponent,
},
],
template: `<smart-table [options]="options" />`,
})
export class TableCustomExampleComponent {
options: ITableOptions = {
title: 'Users',
description: 'Everyone with access to this workspace.',
striped: true,
columns: [
{ key: 'name', label: 'Name' },
{ key: 'role', label: 'Role' },
{ key: 'email', label: 'Email' },
{ key: 'seats', label: 'Seats', align: 'right' },
],
rows: [
{
name: 'Lindsay Walton',
role: 'Front-end Developer',
email: 'lindsay.walton@example.com',
seats: 3,
},
{
name: 'Courtney Henry',
role: 'Designer',
email: 'courtney.henry@example.com',
seats: 1,
},
{
name: 'Tom Cook',
role: 'Director of Product',
email: 'tom.cook@example.com',
seats: 8,
},
],
};
}
Source
The component lives in packages/shared/angular/src/lib/components/table and is documented for Claude Code by the angular-components-table skill.