DataTable
Introduction
Section titled “Introduction”DataTable combines a native HTML table with optional sorting, search, and pagination. Compose its ten components to control the columns, cell content, labels, and row actions.
When to use
Section titled “When to use”Use DataTable for structured records such as users, projects, or orders. For a small static comparison, omit search and sortable headers. For editable spreadsheets or virtualized datasets, use a dedicated grid solution: DataTable uses ordinary table navigation and renders every supplied row.
Quick example
Section titled “Quick example”Search for “longer” to find a record beyond the first page, sort Users, or move to the next page. Long project names wrap while column widths stay stable.
Showing 1 to 4 of 4 entries
| Acme Starter | 1,280 | 2026-02-10 |
|---|---|---|
| Beacon Dashboard | 642 | 2026-01-22 |
| Citrine Docs | 2,890 | 2026-02-17 |
| A project with a longer descriptive name | 35 | 2026-03-01 |
---import { DataTable, DataTableHeader, DataTableColumn, DataTableBody, DataTableRow, DataTableCell, DataTableSearch, DataTableInfo, DataTablePagination,} from 'accessible-astro-components'
const projects = [ { name: 'Acme Starter', users: 1280, updated: '2026-02-10' }, { name: 'Beacon Dashboard', users: 642, updated: '2026-01-22' }, { name: 'Citrine Docs', users: 2890, updated: '2026-02-17' }, { name: 'A project with a longer descriptive name', users: 35, updated: '2026-03-01' },]---
<DataTable caption="Projects" pageSize={3} style="--data-table-min-width: 32rem"> <DataTableSearch slot="search" label="Search projects" /> <DataTableInfo slot="info" start={1} end={projects.length} total={projects.length} /> <DataTableHeader> <DataTableColumn sortable width="50%">Project</DataTableColumn> <DataTableColumn sortable numeric width="20%">Users</DataTableColumn> <DataTableColumn sortable width="30%">Updated</DataTableColumn> </DataTableHeader> <DataTableBody> {projects.map((project) => ( <DataTableRow> <DataTableCell as="th">{project.name}</DataTableCell> <DataTableCell numeric sortValue={project.users}> {project.users.toLocaleString('en-US')} </DataTableCell> <DataTableCell sortValue={project.updated}>{project.updated}</DataTableCell> </DataTableRow> ))} </DataTableBody> <DataTablePagination slot="footer" client ariaLabel="Projects pagination" /></DataTable>Install the package with npm install accessible-astro-components and import the components from its root. DataTable is available in version 5.8.0 and later.
Composition and slots
Section titled “Composition and slots”| Component | Rendered element / purpose |
|---|---|
DataTable | Container, captioned table, scroll area, and polite live region |
DataTableHeader | thead containing a single tr |
DataTableColumn | th scope=“col”; adds a native button when sortable |
DataTableBody | tbody |
DataTableRow | tr |
DataTableCell | td, or a th with as=“th” |
DataTableActions | td excluded from search by default |
DataTableSearch | Labeled search input and clear button |
DataTableInfo | Visible result summary |
DataTablePagination | Client pagination buttons or page navigation links |
Use the default slot for DataTableHeader and DataTableBody. Place DataTableSearch in slot="search", DataTableInfo in slot="info" between the search and table, and pagination in slot="footer". Header, row, cell, and action components accept their content in the default slot. Keep each row’s cells aligned with the header columns; the client logic expects a single body and simple columns rather than spanning or nested tables.
Search and pagination
Section titled “Search and pagination”Search uses a case-insensitive substring match across the text of filterable cells. It runs after a 300 ms typing pause; Enter and a submit button are not required. The clear button removes the query, restores all matching records, and returns focus to the input. Use filterable={false} to exclude an individual cell; action cells are excluded by default.
For client pagination, render the complete dataset, set pageSize on DataTable, and add DataTablePagination client. Sorting and filtering operate on all supplied rows before selecting the visible page. Changing the search or sort returns to page one. Client pagination does not fetch data from a server.
For page-reload pagination, omit pageSize and client, render the current page’s records, and supply URLs or a baseUrl:
<DataTablePagination slot="footer" baseUrl="/users" currentPage={2} totalPages={5}/>This generates /users for page one and /users/2, /users/3, and so on. In this mode, built-in search and sorting cover only the rendered page. Implement filtering and sorting on your server if they must cover records that are not present in the document. The component does not provide server request hooks or a public JavaScript API.
Sorting and values
Section titled “Sorting and values”A new sortable column starts ascending. Activating it again switches between ascending and descending. Only one column is sorted at a time. Unsorted columns show a paired arrow; the active column shows a single up or down arrow. Equal values retain their original row order.
Set numeric on both the column and its cells for numeric sorting and alignment. Prefer a raw sortValue for formatted numbers and ISO dates. Search still uses the displayed text, not sortValue.
<DataTableColumn sortable numeric sortKey="revenue">Revenue</DataTableColumn><!-- In the corresponding row: --><DataTableCell numeric sortKey="revenue" sortValue={1234.5}>€1,234.50</DataTableCell>Matching sortKey values select a cell explicitly; otherwise the column position is used. Set sorted="ascending" or sorted="descending" on at most one sortable column to initialize its order. Render rows in that order on the server too if the initial HTML must match the indicated state without JavaScript.
Row actions
Section titled “Row actions”Place buttons or links in DataTableActions. Include the record name in each accessible label, for example aria-label="View details for Albert Flores". A details action can open a Drawer; follow the Drawer documentation for its trigger and dialog content. DataTable itself does not open dialogs or implement deletion.
All components accept class and additional HTML attributes. Attributes go to their rendered wrapper, except DataTableSearch, which forwards additional attributes to the input, and DataTable’s id, which identifies the table. Keep IDs unique across instances.
DataTable
Section titled “DataTable”| Prop | Type | Default | Description |
|---|---|---|---|
caption | string | Required | Accessible table title |
id | string | Generated | ID of the table |
captionHidden | boolean | false | Visually hide the caption, preserving its accessible name |
pageSize | number | — | Positive client page size; omit for unpaginated or server-paginated rows |
initialPage | number | 1 | Initial client page; clamped to the available pages |
labels | object | See below | Sort announcements, hint, and fallback result summaries |
DataTableColumn
Section titled “DataTableColumn”| Prop | Type | Default | Description |
|---|---|---|---|
sortable | boolean | false | Enable the sort button |
sorted | ’ascending’ | ‘descending’ | — | Initial order for a sortable column |
numeric | boolean | false | Numeric sorting and end alignment |
sortKey | string | — | Match a row cell with this key |
width | string | — | CSS length or percentage; unspecified columns share remaining space |
DataTableCell
Section titled “DataTableCell”| Prop | Type | Default | Description |
|---|---|---|---|
as | ’td’ | ‘th' | 'td’ | Cell element; use th for row headings |
scope | ’row’ | ‘col’ | ‘rowgroup’ | ‘colgroup' | 'row’ for th | Header scope; ignored for td |
numeric | boolean | false | End alignment and tabular numerals |
sortValue | string | number | Cell text | Unformatted value used for sorting |
sortKey | string | — | Match a column key |
filterable | boolean | true | Include displayed cell text in search |
DataTableSearch
Section titled “DataTableSearch”| Prop | Type | Default | Description |
|---|---|---|---|
id | string | Generated | Input ID |
label | string | ’Search’ | Input label |
showLabel | boolean | true | Set false to visually hide the label |
placeholder | string | ’Search entries…’ | Input hint; does not replace the label |
clearLabel | string | ’Clear search’ | Accessible name of the clear button |
DataTableInfo
Section titled “DataTableInfo”| Prop | Type | Default | Description |
|---|---|---|---|
start, end, total | number | Required | Server-rendered summary values; use zero for an empty dataset |
template | string | Showing {start} to {end} of {total} entries | Unfiltered summary |
filteredTemplate | string | Showing {start} to {end} of {filtered} entries matching “{query}“ | Matching result summary |
emptyLabel | string | No entries matching “{query}“ | No-match summary |
The client updates the summary after initialization. For client pagination, provide the full rendered range initially so the summary also matches the rows when JavaScript is unavailable. Supported replacement tokens are {start}, {end}, {total}, {filtered}, and {query} during client updates; the server-rendered template supports {start}, {end}, and {total}.
DataTablePagination
Section titled “DataTablePagination”| Prop | Type | Default | Description |
|---|---|---|---|
client | boolean | false | Use client buttons with DataTable pageSize |
pageTemplate | string | Page {page} of {pages} | Client progress text |
currentPage, totalPages | string | number | 1 | Link-mode page numbers; client state comes from DataTable |
baseUrl | string | — | Generate link-mode routes |
firstPage, previousPage, nextPage, lastPage | string | null | undefined | — | Explicit routes; missing routes fall back to baseUrl or disabled controls |
ariaLabel | string | ’Table pagination’ | Navigation name |
firstPageLabel | string | ’Go to the first page’ | First control label |
previousPageLabel | string | ’Go to the previous page’ | Previous control label |
nextPageLabel | string | ’Go to the next page’ | Next control label |
lastPageLabel | string | ’Go to the last page’ | Last control label |
renderProgress | ({ currentPage, totalPages }) => string | — | Link-mode progress callback |
renderPageLabel | ({ type, page }) => string | — | Link-mode control label callback |
Callback page values are string | number; type is 'first' | 'previous' | 'next' | 'last'. See Pagination for callback examples. Client mode uses pageTemplate and the individual control labels instead.
DataTableHeader, DataTableBody, DataTableRow, and DataTableActions have no additional component-specific props beyond class and HTML attributes.
Translations and announcements
Section titled “Translations and announcements”DataTable labels supports these values:
| Key | Default |
|---|---|
sortAscending | ascending |
sortDescending | descending |
sortAnnouncement | Sorted by {column} {direction} |
sortableHint | Column headers with buttons are sortable. |
results | Showing {start} to {end} of {total} entries |
filteredResults | Showing {start} to {end} of {filtered} entries matching “{query}“ |
emptyResults | No entries matching “{query}” |
The three result labels are fallbacks when DataTableInfo is omitted. When it is present, its templates supply both visible and announced summaries. Translate those templates together with the search and pagination labels. Do not add another live region to DataTableInfo: the table already announces updates through its own polite, atomic region.
Accessibility behavior
Section titled “Accessibility behavior”- Native table, caption, column headers, and optional row headers preserve table relationships.
- Tab and Shift+Tab move between interactive controls. Enter or Space activates sort, clear, and client pagination buttons. There is no spreadsheet-style arrow-key navigation.
- The sorted header exposes
aria-sort; the table announces the column and direction without moving focus away from the button. - Search announces the result summary after its typing delay, including the query and no-match state. Pagination announces the updated range. When a focused page button becomes disabled at a boundary, focus moves to the progress text within the navigation.
- The scroll area enters the tab order when it overflows, allowing keyboard scrolling. Long text wraps, while action labels stay on one line.
- Focus outlines, forced-color styles, and reduced-motion overrides are included. Test colors and focus styling in your consuming theme.
Without JavaScript, all supplied rows remain readable and client pagination stays hidden. Search and sorting require JavaScript; page-reload pagination links work without it. Use server-rendered sorting/filtering when those features must work without scripts.
Styling
Section titled “Styling”Pass a custom class to style a particular table. The class is applied to the outer container; use a global selector to reach it from an Astro style block. Most component selectors use :where() so theme styles can override them easily.
<DataTable caption="Projects" class="project-table"> <!-- Headers and rows --></DataTable>
<style> :global(.project-table) { --data-table-min-width: 32rem; --data-table-border: var(--color-default-border); --data-table-header-surface: var(--color-primary-bg); }</style>.data-table { --data-table-border: var(--color-default-border); --data-table-header-surface: var(--color-primary-bg);}
.project-table { --data-table-min-width: 32rem;}Stable column widths
Section titled “Stable column widths”The table uses fixed layout so sorting and filtering do not resize columns based on their contents. Set width on column headers to control proportions. Long text wraps, including unbroken strings such as emails, rather than being truncated.
Set --data-table-min-width on the root when a dense table needs horizontal scrolling instead of increasingly narrow columns. Reserve enough width for action buttons, which do not wrap.
<DataTable caption="Projects" class="project-table"> <DataTableHeader> <DataTableColumn sortable width="50%">Project</DataTableColumn> <DataTableColumn sortable numeric width="20%">Users</DataTableColumn> <DataTableColumn sortable width="30%">Updated</DataTableColumn> </DataTableHeader> <!-- DataTableBody with matching cells --></DataTable>CSS custom properties
Section titled “CSS custom properties”| Property | Purpose |
|---|---|
—data-table-min-width | Minimum table width; defaults to 100% of its container |
—data-table-border | Outer and search borders; defaults to the shared Input token —color-default-border |
—data-table-divider | Row dividers |
—data-table-surface | Table and cell background |
—data-table-header-surface | Header background |
—data-table-row-hover | Hovered rows and active sort-button background |
—data-table-muted | Headers and result summary text |
—data-table-focus | Component focus outline color |
Related components
Section titled “Related components”- Pagination for page navigation outside a table.
- Drawer for supplementary record details.
- Button for row actions.