DataTable

The DataTable component provides a feature-rich interface for displaying tabular data with sorting, pagination, row selection, and customizable columns.

Getting Started

Define columns, feed the dataset, and let DataTable handle search, sorting, and pagination out of the box.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

import

 

{

 

DataTable

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

import

 

type

 

{

 

DataTableColumn

,

 

DataTablePagination

,

 

DataTableSort

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

import

 

{

people

,

 

type

 

Person

 

}

 

from

 

'../data'

;

 

const

columns

:

 

DataTableColumn

<Person

>

[

]

 

=

 

[

  

{

key

:

 

'name'

,

header

:

 

'Name'

,

accessor

:

 

'name'

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'email'

,

header

:

 

'Email'

,

accessor

:

 

'email'

,

sortable

:

 

true

,

minWidth

:

 

200

 

}

,

  

{

key

:

 

'title'

,

header

:

 

'Role'

,

accessor

:

 

'title'

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'department'

,

header

:

 

'Department'

,

accessor

:

 

'department'

,

sortable

:

 

true

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

sortBy

,

setSortBy

]

 

=

useState

<DataTableSort

[

]

>

(

[

]

)

;

  

const

 

[

pagination

,

setPagination

]

 

=

useState

<DataTablePagination

>

(

{

    page

:

 

1

,

    pageSize

:

 

5

,

    total

:

people

.

length

,

  

}

)

;

 

  

return

 

(

    

<DataTable

      

data

=

{

people

}

      

columns

=

{

columns

}

      

sortBy

=

{

sortBy

}

      

onSortChange

=

{

setSortBy

}

      

pagination

=

{

pagination

}

      

onPaginationChange

=

{

setPagination

}

      searchable

      

searchPlaceholder

=

"Search teammates"

    

/>

  

)

;

}

Column Filters

Mark columns filterable and set filterType to pick the control: an input for text/number/date, a dropdown for select/boolean (options auto-derived from the data when filterOptions is omitted). showColumnFilters renders those controls as a persistent row under the headers; omit it to keep them in each header's filter menu.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

import

 

{

 

DataTable

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

import

 

type

 

{

 

DataTableColumn

,

 

DataTableFilter

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

import

 

{

departmentFilterOptions

,

people

,

statusFilterOptions

,

 

type

 

Person

 

}

 

from

 

'../data'

;

 

const

columns

:

 

DataTableColumn

<Person

>

[

]

 

=

 

[

  

// text → inline text input

  

{

key

:

 

'name'

,

header

:

 

'Name'

,

accessor

:

 

'name'

,

sortable

:

 

true

,

filterable

:

 

true

,

filterType

:

 

'text'

 

}

,

  

// select with explicit options → dropdown

  

{

    key

:

 

'department'

,

    header

:

 

'Department'

,

    accessor

:

 

'department'

,

    sortable

:

 

true

,

    filterable

:

 

true

,

    filterType

:

 

'select'

,

    filterOptions

:

departmentFilterOptions

,

  

}

,

  

{

    key

:

 

'status'

,

    header

:

 

'Status'

,

    accessor

:

 

'status'

,

    sortable

:

 

true

,

    filterable

:

 

true

,

    filterType

:

 

'select'

,

    filterOptions

:

statusFilterOptions

,

  

}

,

  

// boolean → Yes / No / All dropdown

  

{

key

:

 

'remote'

,

header

:

 

'Remote'

,

accessor

:

 

'remote'

,

filterable

:

 

true

,

filterType

:

 

'boolean'

,

cell

:

 

(

v

)

 

=>

 

(

v

?

 

'Yes'

 

:

 

'No'

)

 

}

,

  

// number → inline numeric input

  

{

    key

:

 

'salary'

,

    header

:

 

'Salary'

,

    accessor

:

 

'salary'

,

    sortable

:

 

true

,

    filterable

:

 

true

,

    filterType

:

 

'number'

,

    dataType

:

 

'currency'

,

    align

:

 

'right'

,

  

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

filters

,

setFilters

]

 

=

useState

<DataTableFilter

[

]

>

(

[

]

)

;

 

  

return

 

(

    

<DataTable

      

data

=

{

people

}

      

columns

=

{

columns

}

      

filters

=

{

filters

}

      

onFilterChange

=

{

setFilters

}

      showColumnFilters

      

searchable

=

{

false

}

    

/>

  

)

;

}

Row Selection

Set selectable and wire selectedRows / onSelectionChange to track checked rows. Give getRowId so selection survives sorting and paging.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

import

 

{

 

Block

,

 

DataTable

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

import

 

type

 

{

 

DataTableColumn

,

 

DataTablePagination

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

import

 

{

people

,

 

type

 

Person

 

}

 

from

 

'../data'

;

 

const

columns

:

 

DataTableColumn

<Person

>

[

]

 

=

 

[

  

{

key

:

 

'name'

,

header

:

 

'Name'

,

accessor

:

 

'name'

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'email'

,

header

:

 

'Email'

,

accessor

:

 

'email'

,

sortable

:

 

true

,

minWidth

:

 

200

 

}

,

  

{

key

:

 

'role'

,

header

:

 

'Role'

,

accessor

:

 

'role'

,

sortable

:

 

true

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

pagination

,

setPagination

]

 

=

useState

<DataTablePagination

>

(

{

    page

:

 

1

,

    pageSize

:

 

5

,

    total

:

people

.

length

,

  

}

)

;

  

const

 

[

selectedRows

,

setSelectedRows

]

 

=

useState

<

(

string

 

|

 

number

)

[

]

>

(

[

]

)

;

 

  

return

 

(

    

<Block

fullWidth

>

      

<Text

 

size

=

"sm"

 

color

=

{

selectedRows

.

length

?

 

'primary'

 

:

 

'muted'

}

>

        

{

selectedRows

.

length

?

 

`${selectedRows.length} selected`

 

:

 

'No rows selected'

}

      

</Text

>

 

      

<DataTable

        

data

=

{

people

}

        

columns

=

{

columns

}

        

pagination

=

{

pagination

}

        

onPaginationChange

=

{

setPagination

}

        selectable

        

selectedRows

=

{

selectedRows

}

        

onSelectionChange

=

{

setSelectedRows

}

        

getRowId

=

{

(

row

)

 

=>

row

.

id

}

        

searchable

=

{

false

}

      

/>

    

</Block

>

  

)

;

}

Rich Cells

Combine avatars, chips, and status cues inside custom cell renderers to create a readable, on-brand table.

Loading demo…

import

 

{

 

Avatar

,

 

Chip

,

 

DataTable

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

import

 

type

 

{

 

DataTableColumn

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

import

 

{

people

,

 

type

 

Person

 

}

 

from

 

'../data'

;

 

const

rows

=

people

.

slice

(

0

,

 

5

)

;

 

const

columns

:

 

DataTableColumn

<Person

>

[

]

 

=

 

[

  

{

    key

:

 

'name'

,

    header

:

 

'Teammate'

,

    accessor

:

 

'name'

,

    sortable

:

 

true

,

    cell

:

 

(

_value

,

row

)

 

=>

 

(

      

<Avatar

        

size

=

"sm"

        

fallback

=

{

row

.

name

          

.

split

(

' '

)

          

.

map

(

(

part

)

 

=>

part

[

0

]

)

          

.

join

(

''

)

}

        

label

=

{

<Text

 

weight

=

"semibold"

>

{

row

.

name

}

</Text

>

}

        

description

=

{

<Text

 

variant

=

"small"

 

color

=

"muted"

>

{

row

.

title

}

</Text

>

}

        

gap

=

{

8

}

      

/>

    

)

,

  

}

,

  

{

    key

:

 

'team'

,

    header

:

 

'Team'

,

    accessor

:

 

'team'

,

    sortable

:

 

true

,

    cell

:

 

(

value

)

 

=>

 

(

      

<Chip

 

size

=

"xs"

 

color

=

"primary"

 

variant

=

"light"

>

        

{

value

}

      

</Chip

>

    

)

,

  

}

,

  

{

    key

:

 

'status'

,

    header

:

 

'Status'

,

    accessor

:

 

'status'

,

    sortable

:

 

true

,

    cell

:

 

(

value

:

 

Person

[

'status'

]

)

 

=>

 

(

      

<Text

        

color

=

{

value

===

 

'inactive'

 

?

 

'error'

 

:

value

===

 

'pending'

 

?

 

'warning'

 

:

 

'success'

}

        

weight

=

"semibold"

      

>

        

{

value

.

charAt

(

0

)

.

toUpperCase

(

)

 

+

value

.

slice

(

1

)

}

      

</Text

>

    

)

,

  

}

,

  

{

    key

:

 

'performance'

,

    header

:

 

'Score'

,

    accessor

:

 

'performance'

,

    sortable

:

 

true

,

    align

:

 

'right'

,

    cell

:

 

(

value

)

 

=>

 

<Text

 

weight

=

"semibold"

>

{

value

.

toFixed

(

1

)

}

</Text

>

,

  

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<DataTable

      

data

=

{

rows

}

      

columns

=

{

columns

}

      

density

=

"comfortable"

      

variant

=

"striped"

      

searchable

=

{

false

}

    

/>

  

)

;

}

Expandable Rows

Provide expandedRows, update them via onExpandedRowsChange, and use expandableRowRender to reveal supporting context.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

import

 

{

 

Block

,

 

DataTable

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

import

 

type

 

{

 

DataTableColumn

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

import

 

{

projects

,

 

type

 

Project

 

}

 

from

 

'../data'

;

 

const

columns

:

 

DataTableColumn

<Project

>

[

]

 

=

 

[

  

{

key

:

 

'name'

,

header

:

 

'Project'

,

accessor

:

 

'name'

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'owner'

,

header

:

 

'Owner'

,

accessor

:

 

'owner'

,

sortable

:

 

true

 

}

,

  

{

    key

:

 

'budget'

,

    header

:

 

'Budget'

,

    accessor

:

 

'budget'

,

    align

:

 

'right'

,

    sortable

:

 

true

,

    dataType

:

 

'currency'

,

  

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

expandedRows

,

setExpandedRows

]

 

=

useState

<

(

string

 

|

 

number

)

[

]

>

(

[

projects

[

0

]

.

id

]

)

;

 

  

return

 

(

    

<DataTable

      

data

=

{

projects

}

      

columns

=

{

columns

}

      

getRowId

=

{

(

row

)

 

=>

row

.

id

}

      

expandedRows

=

{

expandedRows

}

      

onExpandedRowsChange

=

{

setExpandedRows

}

      

expandableRowRender

=

{

(

project

)

 

=>

 

(

        

<Block

 

p

=

"md"

>

          

<Text

 

color

=

"muted"

>

{

project

.

summary

}

</Text

>

        

</Block

>

      

)

}

      

searchable

=

{

false

}

    

/>

  

)

;

}

Grouping & Totals

Set groupBy to a column key to group rows under collapsible group-header rows. Add aggregate (sum, avg, min, max, count, or a function) to any column to show its per-group total in the group header, and set showFooterTotals for a grand-total footer row aligned to the same columns. Grouping spans all filtered rows, so client pagination is bypassed while it is active.

Loading demo…

import

 

{

 

DataTable

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

import

 

type

 

{

 

DataTableColumn

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

import

 

{

sales as rows

,

 

type

 

Sale

 

}

 

from

 

'../data'

;

 

const

columns

:

 

DataTableColumn

<Sale

>

[

]

 

=

 

[

  

{

key

:

 

'region'

,

header

:

 

'Region'

,

accessor

:

 

'region'

 

}

,

  

{

key

:

 

'rep'

,

header

:

 

'Rep'

,

accessor

:

 

'rep'

,

aggregate

:

 

'count'

 

}

,

  

{

key

:

 

'product'

,

header

:

 

'Product'

,

accessor

:

 

'product'

 

}

,

  

{

key

:

 

'units'

,

header

:

 

'Units'

,

accessor

:

 

'units'

,

dataType

:

 

'number'

,

align

:

 

'right'

,

aggregate

:

 

'sum'

 

}

,

  

{

key

:

 

'revenue'

,

header

:

 

'Revenue'

,

accessor

:

 

'revenue'

,

dataType

:

 

'currency'

,

align

:

 

'right'

,

aggregate

:

 

'sum'

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<DataTable

      

data

=

{

rows

}

      

columns

=

{

columns

}

      

groupBy

=

"region"

      showFooterTotals

      

footerLabel

=

"All regions"

      

searchable

=

{

false

}

      

showColumnVisibilityManager

=

{

false

}

    

/>

  

)

;

}

Fixed height & sticky columns

Pass a fixed height to cap the table's size — the header row stays pinned while the body scrolls, so a long list fits a constrained panel without paginating. Pin columns to the edges with sticky: 'left' or sticky: 'right' so they stay put while the rest scroll horizontally; give each pinned column an explicit numeric width so its frozen offset lines up. Sticky positioning is web-only (a no-op on native).

Loading demo…

import

 

{

 

DataTable

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

import

 

type

 

{

 

DataTableColumn

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

type

 

Server

 

=

 

{

  id

:

 

number

;

  host

:

 

string

;

  region

:

 

string

;

  cpu

:

 

string

;

  memory

:

 

string

;

  uptime

:

 

string

;

  status

:

 

'healthy'

 

|

 

'degraded'

 

|

 

'offline'

;

}

;

 

const

 

REGIONS

 

=

 

[

'us-east-1'

,

 

'us-west-2'

,

 

'eu-west-1'

,

 

'ap-south-1'

]

;

const

 

STATUSES

:

 

Server

[

'status'

]

[

]

 

=

 

[

'healthy'

,

 

'degraded'

,

 

'offline'

]

;

 

const

rows

:

 

Server

[

]

 

=

 

Array

.

from

(

{

length

:

 

40

 

}

,

 

(

_

,

i

)

 

=>

 

(

{

  id

:

i

+

 

1

,

  host

:

 

`node-${String(i + 1).padStart(2, '0')}.cluster.internal`

,

  region

:

 

REGIONS

[

i

%

 

REGIONS

.

length

]

,

  cpu

:

 

`${((i * 7) % 90) + 5}%`

,

  memory

:

 

`${((i * 13) % 80) + 10}%`

,

  uptime

:

 

`${(i % 30) + 1}d`

,

  status

:

 

STATUSES

[

i

%

 

STATUSES

.

length

]

,

}

)

)

;

 

const

columns

:

 

DataTableColumn

<Server

>

[

]

 

=

 

[

  

// Pinned left, so the host stays visible while the rest scroll horizontally.

  

{

key

:

 

'host'

,

header

:

 

'Host'

,

accessor

:

 

'host'

,

sticky

:

 

'left'

,

width

:

 

240

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'region'

,

header

:

 

'Region'

,

accessor

:

 

'region'

,

width

:

 

160

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'cpu'

,

header

:

 

'CPU'

,

accessor

:

 

'cpu'

,

width

:

 

120

,

align

:

 

'right'

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'memory'

,

header

:

 

'Memory'

,

accessor

:

 

'memory'

,

width

:

 

120

,

align

:

 

'right'

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'uptime'

,

header

:

 

'Uptime'

,

accessor

:

 

'uptime'

,

width

:

 

120

,

align

:

 

'right'

 

}

,

  

{

key

:

 

'status'

,

header

:

 

'Status'

,

accessor

:

 

'status'

,

sticky

:

 

'right'

,

width

:

 

140

,

sortable

:

 

true

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<DataTable

      

data

=

{

rows

}

      

columns

=

{

columns

}

      

getRowId

=

{

(

row

)

 

=>

row

.

id

}

      

height

=

{

320

}

      

fullWidth

=

{

false

}

      

searchable

=

{

false

}

    

/>

  

)

;

}

Server-side pagination

Set manualPagination when the data comes from a paginated API. The data prop is treated as the already-fetched current page — the table does no client-side slicing, filtering, or sorting — and pagination.total drives the page count and "X-Y of N" summary. The sort, filter, search, and page controls still fire their callbacks (onSortChange, onFilterChange, onSearchChange, onPaginationChange) so you can refetch. Pair it with loading to show the skeleton during each fetch.

Loading demo…

import

 

{

useEffect

,

useState

}

 

from

 

'react'

;

import

 

{

 

DataTable

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

import

 

type

 

{

 

DataTableColumn

,

 

DataTablePagination

,

 

DataTableSort

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

type

 

Order

 

=

 

{

  id

:

 

number

;

  customer

:

 

string

;

  product

:

 

string

;

  amount

:

 

number

;

}

;

 

// Pretend this table lives on a server; the component only ever sees one page.

const

 

DB

:

 

Order

[

]

 

=

 

Array

.

from

(

{

length

:

 

137

 

}

,

 

(

_

,

i

)

 

=>

 

(

{

  id

:

i

+

 

1

,

  customer

:

 

`Customer ${String(i + 1).padStart(3, '0')}`

,

  product

:

 

[

'Starter'

,

 

'Pro'

,

 

'Team'

,

 

'Enterprise'

]

[

i

%

 

4

]

,

  amount

:

 

Math

.

round

(

(

(

i

*

 

37

)

 

%

 

900

)

 

+

 

100

)

,

}

)

)

;

 

// Simulate an API endpoint: GET /orders?page&pageSize&sort

function

 

fetchOrders

(

  page

:

 

number

,

  pageSize

:

 

number

,

  sort

?:

 

DataTableSort

)

:

 

Promise

<

{

rows

:

 

Order

[

]

;

total

:

 

number

 

}

>

 

{

  

return

 

new

 

Promise

(

(

resolve

)

 

=>

 

{

    

setTimeout

(

(

)

 

=>

 

{

      

const

sorted

=

 

[

.

.

.

DB

]

;

      

if

 

(

sort

?

.

direction

)

 

{

        sorted

.

sort

(

(

a

,

b

)

 

=>

 

{

          

const

av

=

a

[

sort

.

column as keyof

Order

]

;

          

const

bv

=

b

[

sort

.

column as keyof

Order

]

;

          

const

cmp

=

typeof av

===

 

'number'

 

&&

typeof bv

===

 

'number'

 

?

av

-

bv

:

 

String

(

av

)

.

localeCompare

(

String

(

bv

)

)

;

          

return

sort

.

direction

===

 

'desc'

 

?

 

-

cmp

:

cmp

;

        

}

)

;

      

}

      

const

start

=

 

(

page

-

 

1

)

 

*

pageSize

;

      

resolve

(

{

rows

:

sorted

.

slice

(

start

,

start

+

pageSize

)

,

total

:

 

DB

.

length

}

)

;

    

}

,

 

500

)

;

  

}

)

;

}

 

const

columns

:

 

DataTableColumn

<Order

>

[

]

 

=

 

[

  

{

key

:

 

'id'

,

header

:

 

'Order'

,

accessor

:

 

'id'

,

sortable

:

 

true

,

dataType

:

 

'number'

 

}

,

  

{

key

:

 

'customer'

,

header

:

 

'Customer'

,

accessor

:

 

'customer'

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'product'

,

header

:

 

'Plan'

,

accessor

:

 

'product'

,

sortable

:

 

true

 

}

,

  

{

key

:

 

'amount'

,

header

:

 

'Amount'

,

accessor

:

 

'amount'

,

sortable

:

 

true

,

dataType

:

 

'currency'

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

rows

,

setRows

]

 

=

useState

<Order

[

]

>

(

[

]

)

;

  

const

 

[

loading

,

setLoading

]

 

=

 

useState

(

true

)

;

  

const

 

[

sortBy

,

setSortBy

]

 

=

useState

<DataTableSort

[

]

>

(

[

]

)

;

  

const

 

[

pagination

,

setPagination

]

 

=

useState

<DataTablePagination

>

(

{

page

:

 

1

,

pageSize

:

 

10

,

total

:

 

0

 

}

)

;

 

  

// Refetch whenever the page, page size, or sort changes.

  

useEffect

(

(

)

 

=>

 

{

    

let

cancelled

=

 

false

;

    

setLoading

(

true

)

;

    

fetchOrders

(

pagination

.

page

,

pagination

.

pageSize

,

sortBy

[

0

]

)

.

then

(

(

res

)

 

=>

 

{

      

if

 

(

cancelled

)

 

return

;

      

setRows

(

res

.

rows

)

;

      

setPagination

(

(

p

)

 

=>

 

(

p

.

total

===

res

.

total

?

p

:

 

{

 

.

.

.

p

,

total

:

res

.

total

}

)

)

;

      

setLoading

(

false

)

;

    

}

)

;

    

return

 

(

)

 

=>

 

{

      cancelled

=

 

true

;

    

}

;

  

}

,

 

[

pagination

.

page

,

pagination

.

pageSize

,

sortBy

]

)

;

 

  

return

 

(

    

<DataTable

      

data

=

{

rows

}

      

columns

=

{

columns

}

      

loading

=

{

loading

}

      manualPagination

      

pagination

=

{

pagination

}

      

onPaginationChange

=

{

setPagination

}

      

sortBy

=

{

sortBy

}

      

onSortChange

=

{

setSortBy

}

      

getRowId

=

{

(

row

)

 

=>

row

.

id

}

      

searchable

=

{

false

}

    

/>

  

)

;

}

</>

react-ui-library

100+ accessible, themeable components that work seamlessly across iOS, Android, and Web.

A Platform Blocks product.