Enhancement
DataTable
Sorting, filtering and pagination for an existing table
- Import
@parallelogram-js/core/components/DataTable- Selector
[data-datatable]- Stylesheet
@parallelogram-js/core/styles/datatable.css
Playground
Bookings
| Guest | Date | Party | Spend |
|---|---|---|---|
| Ada Lovelace | 2 October | 4 | $320 |
| Grace Hopper | 28 September | 2 | $140 |
| Katherine Johnson | 11 October | 6 | $1,210 |
| Alan Turing | 30 September | 3 | $265 |
| Hedy Lamarr | 5 October | 2 | $98 |
| Tim Berners-Lee | 19 October | 8 | $1,480 |
<table data-datatable data-datatable-filterable data-datatable-paginate="4">
<thead>
<tr>
<th data-sort="name">Guest</th>
<th data-sort="date" data-sort-type="date">Date</th>
<th data-sort="party" data-sort-type="number">Party</th>
<th data-sort="spend" data-sort-type="number">Spend</th>
</tr>
</thead>
<tbody>
<tr><td>Ada Lovelace</td><td data-sort-value="2026-10-02">2 October</td><td>4</td><td>$320</td></tr>
<tr><td>Grace Hopper</td><td data-sort-value="2026-09-28">28 September</td><td>2</td><td>$140</td></tr>
<tr><td>Katherine Johnson</td><td data-sort-value="2026-10-11">11 October</td><td>6</td><td>$1,210</td></tr>
<tr><td>Alan Turing</td><td data-sort-value="2026-09-30">30 September</td><td>3</td><td>$265</td></tr>
<tr><td>Hedy Lamarr</td><td data-sort-value="2026-10-05">5 October</td><td>2</td><td>$98</td></tr>
<tr><td>Tim Berners-Lee</td><td data-sort-value="2026-10-19">19 October</td><td>8</td><td>$1,480</td></tr>
</tbody>
</table>
Use the example to see the events it sends.
| Guest | Date | Party | Spend |
|---|---|---|---|
| Ada Lovelace | 2 October | 4 | $320 |
| Grace Hopper | 28 September | 2 | $140 |
| Katherine Johnson | 11 October | 6 | $1,210 |
| Alan Turing | 30 September | 3 | $265 |
| Hedy Lamarr | 5 October | 2 | $98 |
| Tim Berners-Lee | 19 October | 8 | $1,480 |
About
Sortable headers get a button and aria-sort, following the WAI-ARIA sortable table example. Values are read once when the table mounts: numbers are the first number in the cell, ignoring currency symbols, units and group separators, using the decimal separator of the table's lang, and negative after a minus sign or inside parentheses, dates are parsed, text is compared in natural order ("Item 2" before "Item 10"), and blank values always sort last.
Filtering keeps the current sort, pagination is a labelled navigation region with a window of page numbers, hidden while every row fits on one page, and a status message announces which rows are showing after each change. The pagination and status text can be translated through attributes. The original rows are moved rather than copied, and unmounting puts the table back as it was.
Usage
Register the component and the framework loads it the first time a page contains [data-datatable].
import { Parallelogram } from '@parallelogram-js/core';
const app = Parallelogram.create();
app.components.add('[data-datatable]', () => import('@parallelogram-js/core/components/DataTable'));
app.run();
The framework is optional. DataTable.enhanceAll() mounts the component on every matching element by itself, for a page that wants one enhancement rather than a framework:
import DataTable from '@parallelogram-js/core/components/DataTable';
DataTable.enhanceAll();
It takes a selector and the component's options, and returns the instance it mounted them with. What it leaves out is what the framework adds: loading a component only on the pages that use it, mounting again after a page swap, and ordering with dependsOn.
Its styles come from the package stylesheet:
@import '@parallelogram-js/core/styles/datatable.css';
Attributes
| Name | Type | Default | Description |
|---|---|---|---|
data-datatable | flag | none | Marks the table |
data-datatable-sortable | boolean | true | Sort by the headers marked data-sort For every instance, DataTable.defaults.sortable. |
data-datatable-filterable | boolean | false | Add a search box above the table For every instance, DataTable.defaults.filterable. |
data-datatable-paginate | string | none | A page size such as 10, or true to use data-datatable-page-size |
data-datatable-page-size | number | 10 | Rows per page For every instance, DataTable.defaults.pageSize. |
data-datatable-search-delay | number | 300 | Milliseconds to wait after typing before filtering For every instance, DataTable.defaults.searchDelay. |
data-datatable-search-label | string | Search | The search box's label For every instance, DataTable.defaults.searchLabel. |
data-datatable-search-placeholder | string | Search table… | The search box's placeholder For every instance, DataTable.defaults.searchPlaceholder. |
data-datatable-empty-message | string | No matching rows | The row shown when nothing matches For every instance, DataTable.defaults.emptyMessage. |
data-datatable-status-message | string | Showing {from}–{to} of {total} rows{sort} | The status shown and announced after each change, with {from}, {to}, {total} and {sort} For every instance, DataTable.defaults.statusMessage. |
data-datatable-sort-status | string | , sorted by {column}, {direction} | The {sort} part of the status while a column is sorted, with {column} and {direction}; without it the status reads the same before and after a sort For every instance, DataTable.defaults.sortStatus. |
data-datatable-sort-ascending | string | ascending | The {direction} of an ascending sort For every instance, DataTable.defaults.sortAscending. |
data-datatable-sort-descending | string | descending | The {direction} of a descending sort For every instance, DataTable.defaults.sortDescending. |
data-datatable-pagination-label | string | Table pagination | The pagination region's label For every instance, DataTable.defaults.paginationLabel. |
data-datatable-previous-text | string | Previous | The previous page button's text For every instance, DataTable.defaults.previousText. |
data-datatable-previous-label | string | Previous page | The previous page button's label For every instance, DataTable.defaults.previousLabel. |
data-datatable-next-text | string | Next | The next page button's text For every instance, DataTable.defaults.nextText. |
data-datatable-next-label | string | Next page | The next page button's label For every instance, DataTable.defaults.nextLabel. |
data-datatable-page-label | string | Page {page} | Each page number button's label, with {page} For every instance, DataTable.defaults.pageLabel. |
data-sort | string | none | Makes the column sortable, naming its key On a header. |
data-sort-type | string | number | date | string | How the column compares On a header. |
data-sort-value | string | none | The value to sort by instead of its text On a cell. |
data-datatable-stateset by the component | mounted | loading | loaded | empty | error | none | The table state |
data-datatable-error-messageset by the component | string | none | Why the last loadData() failed |
Methods
| Name | Signature | Description |
|---|---|---|
loadData | (element: HTMLTableElement, url: string, rowMapper: (item: unknown) => HTMLTableRowElement) => Promise<void> | Replace the rows with rows made from a JSON array fetched from a URL, one row per item |
sort | (element: HTMLTableElement, column: string, direction?: 'asc' | 'desc') => void | Sort by the column with this data-sort key, ascending unless 'desc' is given |
filter | (element: HTMLTableElement, searchTerm: string) => void | Show only the rows containing the text, and put it in the search box |
goToPage | (element: HTMLTableElement, page: number) => void | Show a page, when the table paginates and the page exists |
clearError | (element: HTMLTableElement) => void | Clear a loadData() error and show the rows again |
Events
| Name | Detail | Description |
|---|---|---|
datatable:mounted | { config: object } | The table was set up DOM event and event bus |
datatable:rendered | { totalRows: number; filteredRows: number; displayedRows: number } | The visible rows changed DOM event and event bus |
datatable:loaded | { url: string; rowCount: number } | loadData() finished with rows DOM event and event bus |
datatable:empty | { url: string; rowCount: number } | loadData() finished without rows DOM event and event bus |
datatable:error | { error: Error; url: string; message: string } | loadData() failed DOM event and event bus |
CSS custom properties
| Name | Default | Description |
|---|---|---|
--datatable-error-default | var(--color-danger-text) | The load error colour used when neither --datatable-error-color nor --brand-danger is set |
--datatable-border-color | none | Borders of the search box and pagination buttons; by default the search box uses --surface-control-border-color and the buttons --color-border-strong |
--datatable-button-bg | transparent | Pagination button background |
--datatable-button-color | inherit | Pagination button text |
--datatable-button-hover-bg | var(--color-hover) | Pagination button background on hover: the hover tint |
--datatable-button-hover-border-color | var(--color-border-strong) | Pagination button border on hover: the strong border colour |
--datatable-current-bg | var(--color-accent-hover) | The current page button: the stronger accent |
--datatable-current-color | var(--color-accent-contrast) | The current page text: the text colour on the accent |
--datatable-error-color | #b91c1c | Load error message text: a darker red on light pages, and --brand-danger or --color-danger on dark pages |
--datatable-header-hover-bg | var(--color-hover) | Sortable header background on hover: the hover tint |
--datatable-muted-color | var(--color-text-muted) | Status, messages and sort icons: the muted text colour |