npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@asphalt-react/data-table

v2.14.0

Published

Data Table

Readme

DataTable

npm npm version

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 objects
  • value: Display label for the column

Optional sorting properties:

  • sortable: Boolean to enable sorting for this column
  • customSort: Function (valueA, valueB, {ascending}) => number to 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 left and right keys must each exist in the header array
  • left and right cannot 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 DataTable
    • activePage: Current active page number
    • activePerPage: Current items per page count
    • totalPages: Total number of pages
    • totalRecords: Total number of records
    • paginationLink: Whether pagination items render as links
    • paginationElement: Element type for pagination items
    • onPaginationChange: Unified handler for both page and per-page changes
    • getTileProps: 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 onChange callback 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 as parameter and the getTileProps function
  • Flexible Totals: Can calculate totals from data array or accept explicit totalPages and totalRecords for 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 |