PrimeNG Table Accessibility
Permalink to "PrimeNG Table Accessibility"PrimeNG’s p-table is a feature-rich Angular table with sorting, filtering, selection, row expansion, frozen columns, virtual scroll and pagination, and PrimeNG documents accessibility for its components, including keyboard behaviour for sortable headers and selection. It renders a semantic table, which works naturally with screen reader table commands. As with every library, the remaining work is context: the table’s name, per-row labels, translated aria strings, meaningful empty templates, and announcements of results.
This guide belongs to Angular data components.
Spec reference
Permalink to "Spec reference"- Semantic table —
p-tablerenderstable,thead,th,td. pSortableColumn— makes a header sortable with keyboard support andaria-sort.- Selection —
p-tableCheckboxandp-tableHeaderCheckbox; accessible names come from the aria locale configuration. PrimeNGConfig/ providePrimeNG translation — aria strings for paginator, filters and selection.- Relevant criteria: SC 4.1.2 (A), SC 4.1.3 (AA), SC 2.4.7 (AA).
When to use — and when not to
Permalink to "When to use — and when not to"Use p-table for Angular apps already on PrimeNG that need many table features. Keep features minimal and verify each.
The misapplication: frozen columns plus scrollable tables plus virtual scroll, all at once, without testing header association — some configurations split the table into multiple tables visually and structurally, and screen readers may lose header relationships.
Annotated code example
Permalink to "Annotated code example"<h2 id="inv-h">Invoices</h2>
<p-table #dt [value]="invoices" dataKey="id" [paginator]="true" [rows]="25"
[(selection)]="selected" [tableStyle]="{ 'min-width': '50rem' }"
[globalFilterFields]="['id','customer']"
(onSort)="announceSort($event)" (onFilter)="announceFilter($event)" (onPage)="announcePage($event)"
[attr.aria-labelledby]="'inv-h'"> <!-- verify it reaches the table element -->
<ng-template pTemplate="caption">
<label for="inv-search">Search invoices</label>
<input id="inv-search" pInputText type="text" (input)="dt.filterGlobal($any($event.target).value, 'contains')" />
</ng-template>
<ng-template pTemplate="header">
<tr>
<th style="width: 3rem"><p-tableHeaderCheckbox ariaLabel="Select all invoices on this page" /></th>
<th pSortableColumn="id">Invoice <p-sortIcon field="id" /></th>
<th pSortableColumn="amount">Amount (EUR) <p-sortIcon field="amount" /></th>
</tr>
</ng-template>
<ng-template pTemplate="body" let-inv>
<tr>
<td><p-tableCheckbox [value]="inv" [ariaLabel]="'Select invoice ' + inv.id" /></td>
<th scope="row">{{ inv.id }}</th>
<td>{{ inv.amount | number:'1.2-2' }}</td>
</tr>
</ng-template>
<ng-template pTemplate="emptymessage">
<tr><td colspan="3">No invoices match the current search.</td></tr>
</ng-template>
</p-table>
announceSort(e: { field: string; order: number }) { this.announcer.announce(`Sorted by ${this.label(e.field)}, ${e.order === 1 ? 'ascending' : 'descending'}`); }
announceFilter(e: { filteredValue: unknown[] }) { this.announcer.announce(`${e.filteredValue.length} invoices shown`); }
announcePage(e: { first: number; rows: number }) { this.announcer.announce(`Page ${e.first / e.rows + 1}`); }
Input names such as ariaLabel on checkbox components vary between PrimeNG versions; check your version’s API.
Keyboard & AT behaviour
Permalink to "Keyboard & AT behaviour"| Interaction | Expected | Announcement |
|---|---|---|
| Tab to sortable header | Focus | Header name and sort state |
| Enter | Sort | aria-sort; “Sorted by Amount, ascending” |
| Space on row checkbox | Select | “Select invoice INV-1042, checkbox, checked” |
| Filter menu | Popup with focus inside | Popup controls labelled |
| Paginator | Labelled buttons | “Page 2” |
Integration context
Permalink to "Integration context"PrimeNG and PrimeVue share many concepts; see PrimeVue DataTable accessibility. Announcements use the CDK LiveAnnouncer. Frozen columns are discussed in frozen columns and screen reader reading order.
Gotchas
Permalink to "Gotchas"Attribute targets. Attributes on p-table may land on the host or a wrapper; inspect the DOM and target the table element (PrimeNG supports pass-through options in recent versions).
Unstyled or custom themes. Focus styles may be lost in custom themes; verify.
Row expansion. Expansion togglers need names with the record and aria-expanded.
Common mistakes
Permalink to "Common mistakes"- Untranslated aria locale strings.
- Generic checkbox labels.
- Over-configuration without testing.
- No announcements.
Adoption checklist for teams
Permalink to "Adoption checklist for teams"- Provide a PrimeNG table preset component with naming, labels and the announcer wired.
- Configure translations for aria strings at the application level.
- Approve a small set of features (sorting, global filter, a few column filters, pagination).
- Require an empty template and an error state.
- Test frozen and scrollable configurations with table navigation before using them.
Version and upgrade notes
Permalink to "Version and upgrade notes"PrimeNG’s theming moved to design tokens and pass-through in newer majors; component input names have changed over time. Re-verify labels, aria strings and focus styles after each upgrade.
Worked example
Permalink to "Worked example"A government case-management app used p-table with frozen columns and menu filters on 14 columns. A JAWS user reported that headers did not match cells in the scrollable area and that filter popups were confusing. The team removed frozen columns from the main table (offering a column chooser instead), reduced column filters to four, translated aria strings into the two official languages, and added announcements. Header association was restored, and the filter workflow became shorter for everyone.
Testing it with a screen reader
Permalink to "Testing it with a screen reader"- Press T to reach the table; confirm the name.
- Move across a row with table navigation; confirm headers and the row header.
- Tab to a sortable header and press Enter; confirm sort state and the message.
- Tab to a row checkbox and press Space; confirm the record name and checked state.
- Open a column filter menu; confirm focus moves in, controls are labelled, and Escape returns focus.
- Use the paginator; confirm labelled buttons and the page message.
- If frozen or scrollable configurations are used, confirm headers still match cells.
Testing checklist
Permalink to "Testing checklist"Design system notes
Permalink to "Design system notes"Maintain PrimeNG presets in the design system with approved features, translations and announcer wiring, and document the configurations that need extra testing.
FAQ
Permalink to "FAQ"Is PrimeNG Table accessible?
PrimeNG documents accessibility support, and the foundation is good. Configuration and testing complete it.
Does pSortableColumn set aria-sort?
Yes in current versions; verify in the DOM.
How do I translate aria labels?
Through PrimeNG’s translation configuration at application level.
Are frozen columns accessible?
They can be, but some scrollable configurations affect structure; test header association with table navigation.
Should I use virtual scroll?
For large client-side data; announce positions and consider pagination for screen reader-heavy workflows.
How do I announce filter results?
Use onFilter and announce filteredValue length through LiveAnnouncer.
Can I use p-table as a grid with cell navigation?
p-table is a table; for spreadsheet-like editing, consider a grid library.
How do I name the global search?
Use a visible label element tied to the input. Placeholders alone disappear when typing starts and are not reliable names.
Does PrimeNG support right-to-left?
PrimeNG supports RTL layouts; verify that sort icons, paginator arrows and column order make sense in RTL and that screen readers read columns in the expected order.
How do I make the row expansion accessible?
Give the toggler a name including the record, set aria-expanded, and place expanded content in a row directly after the parent row.
Related
Permalink to "Related"- mat-table with MatSort — the Material alternative
- PrimeVue DataTable — the Vue sibling
- Frozen columns and reading order — header association