Quick start
Install and first table
Using an AI assistant? Start with the /nat-best-practises skill so generated code follows the table API, state model, accessibility rules, and styling contract.
Recommended Agent Setup
Install the table package and Angular companion peers:
pnpm add ng-advanced-table @angular/aria @angular/cdk
# or: npm install ng-advanced-table @angular/aria @angular/cdk
# or: yarn add ng-advanced-table @angular/aria @angular/cdk
Keep @angular/core and @angular/common (plus @angular/forms and @angular/platform-browser, which @angular/cdk peers on) in your Angular app dependencies, all on the same Angular 22 release, 22.2 or newer. This installs the stable release; see Release Channels for the nightly channel.
Install @angular/aria and @angular/cdk at the same exact version (for example both 22.2.1): @angular/aria pins @angular/cdk to its own exact version, so mismatched versions produce peer-dependency conflicts.
Versioning is not SemVer. The library is still experimental, so there are no major releases yet: breaking changes ship in minor releases (for example 2.16.x to 2.17.0), and patch releases carry fixes, refactors, and docs. Depend on a tilde range such as ~2.16.0, or pin an exact version, rather than a caret range, and read the CHANGELOG before moving to a new minor.
The library works in zoneless applications and renders on the server: browser-only APIs are deferred until the table runs in the browser, so SSR with client hydration needs no extra configuration.
Install the agent skill:
npx skills add VaggelisKa/angular-advanced-table --skill nat-best-practises
Start by asking for the table contract:
Use /nat-best-practises to build an Angular table for invoices with columns for number, customer, status, and total. Start with the row type, row id, columns, and accessibility checks.
Review that contract before generating code.
Manual Setup
If you are wiring the table by hand, start with NatTable inside NatTableSurface.
First Table
Create rows and columns, then render NatTable inside NatTableSurface. Give every table an accessibleName or caption.
import { Component } from '@angular/core';
import { NatTable } from 'ng-advanced-table';
import { NatTableSurface } from 'ng-advanced-table/components';
interface PositionRow {
id: string;
symbol: string;
company: string;
}
@Component({
selector: 'app-positions-table',
imports: [NatTable, NatTableSurface],
template: `
<nat-table-surface>
<nat-table [data]="rows" [columns]="columns" accessibleName="Open positions" />
</nat-table-surface>
`
})
export class PositionsTable {
readonly rows: readonly PositionRow[] = [
{ id: 'pos-1', symbol: 'AAPL', company: 'Apple' },
{ id: 'pos-2', symbol: 'MSFT', company: 'Microsoft' }
];
readonly columns = [
{
accessorKey: 'symbol',
header: 'Symbol',
meta: { label: 'Symbol', rowHeader: true }
},
{
accessorKey: 'company',
header: 'Company',
meta: { label: 'Company' }
}
];
}
Rows with a string or number id property get stable table identity automatically. Use getRowId when identity lives somewhere else.
The table above is visually headless: it inherits page colors and system defaults. For the polished stock look, import 'ng-advanced-table/components/theme.css' once (see /docs/theming), or theme it yourself with --nat-table-* tokens. The stock theme palette uses CSS light-dark() to follow the system color scheme, with a light/dark fallback for browsers without light-dark() support.
Core-Only Scope
For a core-only table, provide NatTableService at the local wrapper that owns the table. Use NatTableSurface for the standard scoped controller and companion-control wiring.
import { Component } from '@angular/core';
import { NatTable, NatTableService } from 'ng-advanced-table';
interface PositionRow {
id: string;
symbol: string;
}
@Component({
selector: 'app-core-only-table',
imports: [NatTable],
providers: [NatTableService],
template: ` <nat-table [data]="rows" [columns]="columns" accessibleName="Core-only positions" /> `
})
export class CoreOnlyTable {
readonly rows: readonly PositionRow[] = [];
readonly columns = [
{
accessorKey: 'symbol',
header: 'Symbol',
meta: { label: 'Symbol', rowHeader: true }
}
];
}
Add Companion Controls
Use ng-advanced-table/components when you want pagination controls, column visibility, horizontal scroll controls, toolbar behavior, header actions, selection checkbox columns, or export actions.
import { Component, signal } from '@angular/core';
import { NatTable, type NatTableUserState } from 'ng-advanced-table';
import {
NatTableColumnVisibility,
NatTablePagination,
NatTableScrollControl,
NatTableSurface,
withNatTableHeaderActions
} from 'ng-advanced-table/components';
interface PositionRow {
id: string;
symbol: string;
desk: string;
price: number;
}
@Component({
selector: 'app-positions-table',
imports: [NatTable, NatTableColumnVisibility, NatTablePagination, NatTableScrollControl, NatTableSurface],
template: `
<nat-table-surface [enableSorting]="true" [enablePinning]="true" [initialState]="initialState">
<nat-table-pagination [pageSizeOptions]="[25, 50, 100]" />
<nat-table [data]="rows()" [columns]="columns" accessibleName="Open positions" />
<nat-table-scroll-control />
<nat-table-column-visibility />
</nat-table-surface>
`
})
export class PositionsTable {
readonly rows = signal<readonly PositionRow[]>([]);
readonly initialState: Partial<NatTableUserState> = {
pagination: { pageIndex: 0, pageSize: 25 }
};
readonly columns = withNatTableHeaderActions([
{
accessorKey: 'symbol',
header: 'Symbol',
meta: { label: 'Symbol', rowHeader: true }
},
{
accessorKey: 'desk',
header: 'Desk',
meta: { label: 'Desk' }
},
{
accessorKey: 'price',
header: 'Price',
meta: { label: 'Price', align: 'end' },
cell: (context) => `$${context.getValue<number>().toFixed(2)}`
}
]);
}
NatTableSurface provides the scoped table controller used by companion controls. Controls inside the same surface can read table state, patch state, bind aria-controls, and resolve locale labels without extra wiring.
Authoring Defaults
Start with these defaults unless the feature needs something else:
| Need | Default choice |
|---|---|
| Table identity | accessibleName or visible caption |
| Row identity | String or number row.id; getRowId for custom, composite, or nested identifiers |
| Column labels | meta.label on every column |
| Row header | meta.rowHeader: true on the primary identifying column |
| Numeric columns | meta.align: 'end' |
| Initial page size or sort | [initialState] |
| Persisting one state slice | [state] with only that slice plus the matching *Change output |
| Search and domain filters | Consumer-owned controls that patch table state |
| Loading, empty, and error | dataStatus plus natTableLoading, natTableEmpty, or natTableError templates |
Release Channels
The package publishes on two npm dist-tags.
| Tag | Install | What it is |
|---|---|---|
latest |
npm i ng-advanced-table |
Scheduled stable releases are cut during the Friday release window after the @next soak gate or its backstop; maintainers can also dispatch a release manually. The default — you get this unless you ask for next. |
next |
npm i ng-advanced-table@next |
A snapshot of main, published on every green commit. |
Nightly versions look like 2.13.0-next.7, where the base is the version the next stable release is currently headed for — it reflects the largest pending change, so a nightly carrying a new feature reads as 2.13.0-next.… and one carrying only fixes as 2.12.2-next.…. The counter starts when the current preview base is first requested, resets when a higher pending change moves the base forward, and stays above any already-published counter for that base. They are semver prereleases, so a range such as ^2.12.0 never resolves to one — opting in is always explicit. Each nightly carries an npm provenance attestation, so you can trace a published version back to the exact commit and CI run it was built from.
Use next to try an unreleased fix or feature, or to check a change against your app before it ships. It is built from the same CI-verified commit as main, but its API is not frozen: anything in a nightly can change or be removed before the next stable release. Pin the exact version rather than tracking the tag if you depend on one in CI.
What is in a nightly
Nightlies have no changelog of their own — a snapshot is a single commit, and the same prose becomes a changelog entry when a stable release is cut. To see what a nightly contains ahead of that, the Nightly workflow run summarises it: it lists the pending release notes and links a full comparison against the last stable tag. The CHANGELOG remains the record for released versions.
Next Steps
- Use
/docs/compositionfor how the core table, the surface, and companion controls fit together, and which element owns which input. - Use
/docs/columnsfor column metadata, sizing, custom cell components, header actions, and row activation. - Use
/docs/statefor controlled and uncontrolled state patterns. - Use
/docs/data-lifecyclefor loading, empty, error, background refresh, and Manual Data Handling. - Use
/docs/filtering-searchfor search and filtering controls. - Use
/docs/row-selectionfor selection checkboxes and bulk state. - Use
/docs/exportfor CSV defaults and custom export handlers. - Use
/docs/themingfor the stock theme and the--nat-table-*tokens. - Use
/docs/accessibilityfor the accessible-name, labeling, and announcement contract. - Use
/docs/keyboard-interactionfor grid keyboard navigation and custom keybindings.