@asphalt-react/data-table
v2.14.0
Published
Data Table
Readme
DataTable
DataTable component displays structured data in a tabular format with support for pagination. It provides an intuitive way to present large datasets with automatic or manual pagination controls. The component handles data validation, row identification, and responsive pagination controls including items per page selection.
DataTable supports two pagination modes: manual pagination where you manage the pagination state externally, and auto-pagination where the component automatically handles pagination internally using the provided data.
Usage
import React from "react"
import { DataTable } from "@asphalt-react/data-table"
const header = [
{ key: "email", value: "Email" },
{ key: "firstName", value: "First Name" },
{ key: "age", value: "Age" }
]
const data = [
{ uniqueId: "user-001", email: "[email protected]", firstName: "John", age: 28 },
{ uniqueId: "user-002", email: "[email protected]", firstName: "Jane", age: 32 },
{ uniqueId: "user-003", email: "[email protected]", firstName: "Bob", age: 45 }
]
function App() {
return (
<DataTable
header={header}
data={data}
identifier="uniqueId"
/>
)
}Data Structure
Header Configuration
The header prop defines the table columns:
const header = [
{ key: "email", value: "Email" },
{ key: "firstName", value: "First Name", sortable: true },
{ key: "age", value: "Age", sortable: true, customSort: (a, b, {ascending: asc}) => asc ? a - b : b - a }
]Each header object requires:
key: Property name in the data objectsvalue: Display label for the column
Optional sorting properties:
sortable: Boolean to enable sorting for this columncustomSort: Function(valueA, valueB, {ascending}) => numberto override default sorting behavior
Data Format
The data prop contains the table rows:
const data = [
{
uniqueId: "user-001",
email: "[email protected]",
firstName: "John",
age: 28
},
{
uniqueId: "user-002",
email: "[email protected]",
firstName: "Jane",
age: 32
}
]Requirements:
- Each object represents a table row
- Object keys should match header keys
- All objects must contain the identifier field
- Values can be strings, numbers, or React nodes
Hover
DataTable component accepts a hover prop to make it look highlighted when on hover.
Sticky Header
DataTable component accepts a stickyHeader prop to keep the dataTable header row sticky while scrolling through the dataTable body.
Columns Pinning
DataTable supports pinning a column to the left or right edge of the dataTable so it remains visible while scrolling horizontally. Pass a pinnedColumns object with left and/or right keys set to the column's key value.
<DataTable
header={header}
data={data}
identifier="uniqueId"
pinnedColumns={{ left: "email" }}
/>Pin both edges simultaneously:
<DataTable
header={header}
data={data}
identifier="uniqueId"
pinnedColumns={{ left: "email", right: "age" }}
/>Constraints:
- The
leftandrightkeys must each exist in theheaderarray leftandrightcannot refer to the same column- On screens narrower than 600px, right pinned columns revert to normal flow automatically
- If a pinned column's rendered width exceeds 75% of the viewport width, pinning is disabled for that column
Loading, Error, and Empty Data States
DataTable supports customizable states for loading, error, and empty data scenarios.
Loading State
Display a loading indicator while data is being fetched:
<DataTable
header={header}
data={data}
identifier="uniqueId"
loading={true}
/>Custom Loading Component
Provide a custom component for the loading state:
const CustomLoader = () => (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "10rem 0rem",
}}
>
<Loader size="large" />
<Text>Loading data...</Text>
</div>
)
<DataTable
header={header}
data={data}
identifier="uniqueId"
loading={true}
loadingComponent={CustomLoader}
/>Error State
Display an error message when data fetching fails:
<DataTable
header={header}
data={[]}
identifier="uniqueId"
error={true}
/>Custom Error Component
Provide a custom component for the error state:
const CustomError = () => (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "10rem 0rem",
}}
>
<Text>Failed to load data. Please try again.</Text>
<Button onClick={refetch}>Retry</Button>
</div>
)
<DataTable
header={header}
data={[]}
identifier="uniqueId"
error={true}
errorComponent={CustomError}
/>Empty State
DataTable will show a message if the data is empty automatically. Example use-case:
<DataTable
header={header}
data={[]}
identifier="uniqueId"
/>Custom Empty Component
Provide a custom component for the empty state:
const CustomEmpty = () => (
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
padding: "10rem 0rem",
}}
>
<Text>No data available</Text>
</div>
)
<DataTable
header={header}
data={[]}
identifier="uniqueId"
emptyComponent={CustomEmpty}
/>Pagination Feature
DataTable supports two distinct pagination modes:
1. Auto-Pagination
When autoPaginate={true} is set, the DataTable automatically:
- Calculates total pages based on data length
- Manages internal pagination state
- Slices the data for the current page
- Handles page and per-page changes internally
This mode is ideal for client-side pagination of static datasets.
import React from "react"
import { DataTable } from "@asphalt-react/data-table"
function App() {
return (
<DataTable
header={header}
data={largeDataset}
identifier="uniqueId"
autoPaginate={true}
paginationLink={false}
/>
)
}2. Manual Pagination
When autoPaginate is false (default), you have full control:
- Provide pre-sliced data for the current page
- Manage pagination state externally
- Handle server-side pagination or complex data fetching
- Control the total pages and records count
This mode is ideal for server-side pagination or when you need custom pagination logic.
import React, { useState } from "react"
import { Link } from "gatsby" // or any other router like react-router
import { DataTable, usePagination } from "@asphalt-react/data-table"
function App() {
const [currentPage, setCurrentPage] = useState(1)
const [currentPerPage, setCurrentPerPage] = useState(10)
const handlePaginationChange = ({ event, page, record }) => {
event.preventDefault()
if (page && page !== currentPage) {
setCurrentPage(page)
}
if (record && record !== currentPerPage) {
setCurrentPerPage(record)
setCurrentPage(1)
}
}
const handleTileProps = ({ page, record }) => ({
to: `?page=${page}&perPage=${record}`,
})
const { getPaginationProps, getSlicedData } = usePagination({
activePage: currentPage,
activePerPage: currentPerPage,
onChange: handlePaginationChange,
getTileProps: handleTileProps,
data: tableData,
as: Link,
link: true,
})
const currentData = getSlicedData(tableData)
return (
<DataTable
header={tableHeader}
data={currentData}
identifier="uniqueID"
{...getPaginationProps()}
/>
)
}Sorting Feature
DataTable supports column sorting with two modes: auto-sorting (client-side) and manual sorting (server-side).
Header Configuration for Sorting
To enable sorting on a column, set sortable: true in the header configuration:
const header = [
{ key: "email", value: "Email" },
{ key: "firstName", value: "First Name", sortable: true },
{
key: "age",
value: "Age",
sortable: true,
// customSort is optional
customSort: (valueA, valueB, {ascending}) => {
const diff = valueA - valueB
return ascending ? diff : -diff
}
}
]1. Auto-Sorting (Client-Side)
When autoSort={true}, the DataTable handles sorting internally:
<DataTable
header={header}
data={data}
identifier="uniqueId"
autoSort={true}
activeSort={{ columnKey: "firstName", ascending: true }}
/>2. Manual Sorting (Server-Side)
For server-side sorting, use the onSort callback:
function App() {
const [sortConfig, setSortConfig] = useState({ columnKey: null, ascending: null })
const handleSort = (event, columnKey, ascending) => {
setSortConfig({ columnKey, ascending })
// Fetch sorted data from server
}
return (
<DataTable
header={header}
data={data}
identifier="uniqueId"
activeSort={sortConfig}
onSort={handleSort}
/>
)
}Custom Sort Props
Use getSortTileProps to add custom attributes to sortable header cells:
<DataTable
header={header}
data={data}
identifier="uniqueId"
autoSort={true}
getSortTileProps={({ columnKey, ascending }) => ({
'data-column': columnKey,
'aria-sort': ascending === true ? 'ascending' : ascending === false ? 'descending' : 'none'
})}
/>Accessibility
- Use tab or shift+tab to navigate among pages.
Custom Link Components
Use router link components for client-side navigation:
import { Link } from "gatsby" // or any other router like react-router
import { DataTable, usePagination } from "@asphalt-react/data-table"
const handleTileProps = ({ page, record }) => ({
to: `?page=${page}&perPage=${record}`,
})
const { getPaginationProps } = usePagination({
activePage: currentPage,
activePerPage: currentPerPage,
getTileProps: handleTileProps,
as: Link,
link: true,
})
<DataTable
// ... other props
{...getPaginationProps()}
/>Hooks
usePagination
Internal hook that provides unified pagination event handlers and prop getters for DataTable components. This hook normalizes the interaction between pagination controls and per-page controls, ensuring consistent event handling across both auto and manual pagination modes.
Returns:
An object with:
getPaginationProps: Object containing all pagination state and handlers to spread onto DataTableactivePage: Current active page numberactivePerPage: Current items per page counttotalPages: Total number of pagestotalRecords: Total number of recordspaginationLink: Whether pagination items render as linkspaginationElement: Element type for pagination itemsonPaginationChange: Unified handler for both page and per-page changesgetTileProps: Unified prop getter for pagination tiles
getSlicedData: Function that slices data array to return only current page items
Parameters
const { getPaginationProps, getSlicedData } = usePagination({
activePage: 1, // Currently active page number
activePerPage: 25, // Current items per page count
data: myData, // Data array to calculate totals from
totalPages: 10, // Optional: Override calculated total pages
totalRecords: 250, // Optional: Override calculated total records
as: Link, // Custom element/component for pagination items
link: true, // Whether pagination items render as links
onChange: ({ event, page, record }) => {
// Unified change handler for both page and per-page changes
console.log('Event:', event, 'Page:', page, 'Record:', record);
},
getTileProps: ({ page, record }) => ({
to: `/users?page=${page}&perPage=${record}`,
'data-testid': `tile-${page}-${record}`
})
});Key Features
- Unified Event Handling: Both page navigation and per-page selection changes call the same
onChangecallback with consistent parameters{ event, page, record } - Prop Getters: Returns specialized prop getter functions that inject proper page/record context based on the control type
- Centralized Props:
getPaginationProps()returns all pagination state and handlers in a single object for easy spreading onto DataTable - Link Integration: Supports custom link components through
asparameter and thegetTilePropsfunction - Flexible Totals: Can calculate totals from data array or accept explicit
totalPagesandtotalRecordsfor server-side pagination - State Normalization: Ensures consistent state management between auto and manual pagination modes
Usage
const [currentPage, setCurrentPage] = useState(1)
const [currentPerPage, setCurrentPerPage] = useState(10)
const handlePaginationChange = ({ event, page, record }) => {
event.preventDefault()
if (page && page !== currentPage) {
setCurrentPage(page)
}
if (record && record !== currentPerPage) {
setCurrentPerPage(record)
setCurrentPage(1)
}
}
const { getPaginationProps, getSlicedData } = usePagination({
activePage: currentPage,
activePerPage: currentPerPage,
onChange: handlePaginationChange,
data: tableData,
link: false,
})
const currentData = getSlicedData(tableData)
return (
<DataTable
header={tableHeader}
data={currentData}
identifier="uniqueID"
{...getPaginationProps()}
/>
)Props
header
Data table header configuration.
Example:
[
{
key: "email",
value: "Email",
},
{
key: "firstName",
value: "First Name",
sortable: true,
},
{
key: "age",
value: "Age",
sortable: true,
customSort: (valueA, valueB, {ascending}) => {
const diff = valueA - valueB
return ascending ? diff : -diff
}
}
]Each header object must contain:
- key: string identifier matching data object properties
- value: string display label for the column header
- sortable (optional): boolean to enable sorting for this column
- customSort (optional): function (valueA, valueB, {ascending}) => number to override default sorting
| type | required | default | | ------- | -------- | ------- | | arrayOf | true | N/A |
identifier
Field name in each data row used as the unique row identifier. This property name must exist in every data object and contain unique values for proper row identification and React key generation.
Example:
identifier="uniqueId"
// With data like:
[
{
uniqueId: "user-001",
name: "John",
email: "[email protected]"
},
{
uniqueId: "user-002",
name: "Jane",
email: "[email protected]"
}
]| type | required | default | | ------ | -------- | ------- | | string | true | N/A |
data
Table data rows to be displayed. Each object represents a row, with keys matching the header configuration. Property names should correspond to header.key values for proper column mapping.
Example:
[
{
uniqueId: "user-001",
email: "[email protected]",
firstName: "John",
age: 28
},
{
uniqueId: "user-002",
email: "[email protected]",
firstName: "Jane",
age: 32
},
{
uniqueId: "user-003",
email: "[email protected]",
firstName: "Bob",
age: 45
}
]Each data object should contain properties that match the keys defined in the header configuration. Values can be strings, numbers, or React nodes for custom cell content.
| type | required | default | | ------- | -------- | ------- | | arrayOf | true | N/A |
autoPaginate
Enables auto pagination logic.
Defaults to false.
| type | required | default | | ---- | -------- | ------- | | bool | false | false |
hover
Makes the row highlight on hover.
Defaults to false.
| type | required | default | | ---- | -------- | ------- | | bool | false | false |
loading
Displays a loading component when set to true.
Defaults to false.
| type | required | default | | ---- | -------- | ------- | | bool | false | false |
loadingComponent
HTML element or React component to replace default loading display component.
| type | required | default | | ----------- | -------- | ------- | | elementType | false | N/A |
error
Whether the table is in an error state. When true, an error component will be displayed.
| type | required | default | | ---- | -------- | ------- | | bool | false | false |
errorComponent
HTML element or React component to replace default error display component.
| type | required | default | | ----------- | -------- | ------- | | elementType | false | N/A |
emptyComponent
HTML element or React component to replace default empty state display component.
| type | required | default | | ----------- | -------- | ------- | | elementType | false | N/A |
activePage
Current active page number for pagination.
Defaults to 1.
| type | required | default | | ------ | -------- | ------- | | number | false | 1 |
activePerPage
Current number of records to display per page.
Controls how many table rows are shown on each page.
Defaults to 10.
| type | required | default | | ------ | -------- | ------- | | number | false | 10 |
totalRecords
Specifies the total number of records in the dataset for pagination calculations and record count display.
| type | required | default | | ------ | -------- | ------- | | number | false | N/A |
totalPages
Specifies the total number of pages available for pagination navigation.
| type | required | default | | ------ | -------- | ------- | | number | false | N/A |
onPaginationChange
Callback function triggered when page or per-page selection changes.
Receives an object with:
- event: browser event
- page: new active page number
- record: new per-page value
| type | required | default | | ---- | -------- | ------- | | func | false | N/A |
paginationElement
HTML element or React component to replace default pagination elements.
Useful for integrating with routing libraries like react-router-dom's Link.
Defaults to "a".
| type | required | default | | ----------- | -------- | ------- | | elementType | false | "a" |
paginationLink
Renders pagination tiles as anchor elements when set to true.
Defaults to true.
| type | required | default | | ---- | -------- | ------- | | bool | false | true |
getTileProps
Function to generate additional props for pagination tiles. Adds custom attributes like href, id, data-attributes, etc.
Receives an object with:
- page: new active page number
- record: new per-page value
| type | required | default | | ---- | -------- | ------- | | func | false | N/A |
autoSort
Enables auto (client-side) sorting.
Sorts data on the client side when set to true.
When false, sorting must be handled externally via onSort callback.
Defaults to false.
| type | required | default | | ---- | -------- | ------- | | bool | false | false |
activeSort
Applies initial sorting configuration when the table first renders. Specifies which column to sort by default and the sort direction.
Object properties:
- columnKey: string - Must match a header key with sortable: true
- ascending: boolean - true for ascending order, false for descending order
Example:
activeSort={{ columnKey: "firstName", ascending: true }}| type | required | default | | ----- | -------- | ------- | | shape | false | N/A |
onSort
Callback function for remote/server-side sorting. Fires when a sortable header is clicked for remote/server-side sorting. Provides the column key and the sort direction in an object.
@param {Object} sortConfig - Sort configuration object @param {string} sortConfig.columnKey - The key of the column being sorted @param {boolean} sortConfig.ascending - True for ascending order, false for descending
Example:
onSort={({ columnKey, ascending }) => {
console.log(`Sort ${columnKey} in ${ascending ? 'ascending' : 'descending'} order`);
// Fetch sorted data from server
}}| type | required | default | | ---- | -------- | ------- | | func | false | N/A |
getSortTileProps
Function to generate additional props for sortable header cells.
Adds custom attributes like data-attributes, aria-labels, etc.
Use this prop together with activeSort to ensure correct sort state is passed.
@param {Object} sortState - Sort state object @param {string} sortState.columnKey - The key of the column @param {boolean|null} sortState.ascending - True for ascending, false for descending, null for unsorted
Example:
getSortTileProps={({ columnKey, ascending }) => ({
'data-column': columnKey,
'aria-sort': ascending === true ? 'ascending' : ascending === false ? 'descending' : 'none'
})}| type | required | default | | ---- | -------- | ------- | | func | false | N/A |
stickyHeader
Enables sticky behaviour for header row
| type | required | default | | ---- | -------- | ------- | | bool | false | false |
pinnedColumns
Pins columns to the left or right edge of the table.
Must provide at least one of left or right, or both.
Specify the column key for each side.
| type | required | default | | ----- | -------- | ------- | | shape | false | N/A |
