DataGrid

Data grids are used to organize lists of high density data.

A advanced data grid component that supports sorting, selections, filters and pagination.

Import#

import {
DataGrid,
DataGridPagination,
useColumns,
useColumnVisibility,
} from '@saas-ui-pro/react'

Usage#

DataGrid uses @tanstack/react-table v8 internally, and supports all useTable props, you can find the docs here.

Basic DataGrid#

import { Button } from '@chakra-ui/react'
import {
Page,
PageHeader,
DataGrid,
DataGridPagination,
} from '@saas-ui-pro/react'
export default function ListPage() {
const columns = React.useMemo(() => {
return [
{
accessorKey: 'name',
header: 'Name',
size: 200,
},
{
accessorKey: 'email',
header: 'Email',
},
{
accessorKey: 'company',
header: 'Company',
},
{
accessorKey: 'country',
header: 'Country',
},
{
accessorKey: 'employees',
header: 'Employees',
meta: {
isNumeric: true,
},
},
{
id: 'action',
disableSortBy: true,
disableGlobaFilter: true,
header: '',
cell: () => (
<>
<Button size="xs">Edit</Button>
</>
),
size: 100,
},
]
}, [])
return (
<Page height="400px">
<PageHeader title="Customers" />
<PageBody p="0" contentWidth="full">
<DataGrid
columns={columns}
data={dataTable.data}
isSortable
isSelectable
isHoverable
>
<DataGridPagination />
</DataGrid>
</PageBody>
</Page>
)
}

Sticky headers are enabled by default, but can be disabled by passing stickyHeader={false} to DataGrid.

Use style props to customize the header styles.

import { Button, Card } from '@chakra-ui/react'
import { DataGrid, DataGridPagination } from '@saas-ui-pro/react'
export default function ListPage() {
const columns = React.useMemo(() => {
return [
{
accessorKey: 'name',
header: 'Name',
size: 200,
},
{
accessorKey: 'email',
header: 'Email',
},
{
accessorKey: 'company',
header: 'Company',
},
{
accessorKey: 'country',
header: 'Country',
},
{
accessorKey: 'employees',
header: 'Employees',
meta: {
isNumeric: true,
},
},
{
id: 'action',
disableSortBy: true,
disableGlobaFilter: true,
header: '',
cell: () => (
<>
<Button size="xs">Edit</Button>
</>
),
size: 100,
},
]
}, [])
return (
<Card overflow="hidden">
<DataGrid
columns={columns}
data={dataTable.data}
stickyHeader={false}
sx={{
'& thead': {
bg: 'gray.50',
_dark: {
bg: 'gray.900',
},
},
}}
>
<DataGridPagination />
</DataGrid>
</Card>
)
}

Column resizing#

Column resizing is disabled by default, but can be enabled by passing columnResizeEnabled to DataGrid.

import { Button, Card } from '@chakra-ui/react'
import { DataGrid, DataGridPagination } from '@saas-ui-pro/react'
export default function ListPage() {
const columns = React.useMemo(() => {
return [
{
accessorKey: 'name',
header: 'Name',
size: 200,
},
{
accessorKey: 'email',
header: 'Email',
},
{
accessorKey: 'company',
header: 'Company',
},
{
accessorKey: 'country',
header: 'Country',
},
{
accessorKey: 'employees',
header: 'Employees',
meta: {
isNumeric: true,
},
},
{
id: 'action',
disableSortBy: true,
disableGlobaFilter: true,
disableResizing: true,
header: '',
cell: () => (
<>
<Button size="xs">Edit</Button>
</>
),
size: 100,
},
]
}, [])
return (
<Card overflow="hidden">
<DataGrid columns={columns} data={dataTable.data} columnResizeEnabled>
<DataGridPagination />
</DataGrid>
</Card>
)
}

Pinned columns#

Pinned columns are columns that are always visible when scrolling horizontally. DataGrid supports both left and right pinned columns.

To pin a column, use the columnPinning prop in intialState or state props. Setting it in state prop will allow you to change the pinned columns dynamically using React state.

import { Button, Card } from '@chakra-ui/react'
import { DataGrid, DataGridPagination } from '@saas-ui-pro/react'
export default function ListPage() {
const columns = React.useMemo(() => {
return [
{
accessorKey: 'name',
header: 'Name',
size: 200,
},
{
accessorKey: 'email',
header: 'Email',
},
{
accessorKey: 'company',
header: 'Company',
},
{
accessorKey: 'country',
header: 'Country',
},
{
accessorKey: 'employees',
header: 'Employees',
meta: {
isNumeric: true,
},
},
{
id: 'action',
disableSortBy: true,
disableGlobaFilter: true,
header: '',
cell: () => (
<>
<Button size="xs">Edit</Button>
</>
),
size: 100,
},
]
}, [])
return (
<Card overflow="hidden">
<DataGrid
columns={columns}
data={dataTable.data}
initialState={{
columnPinning: {
left: ['selection', 'name'],
right: ['action'],
},
}}
>
<DataGridPagination />
</DataGrid>
</Card>
)
}

With BulkActions#

import { Button } from '@chakra-ui/react'
import {
Page,
PageHeader,
DataGrid,
DataGridPagination,
BulkActions,
} from '@saas-ui-pro/react'
export default function ListPage() {
const [selections, setSelections] = React.useState([])
const columns = React.useMemo(() => {
return [
{
accessorKey: 'name',
header: 'Name',
size: 200,
},
{
accessorKey: 'email',
header: 'Email',
},
{
accessorKey: 'company',
header: 'Company',
},
{
accessorKey: 'country',
header: 'Country',
},
{
accessorKey: 'employees',
header: 'Employees',
isNumeric: true,
},
{
id: 'action',
disableSortBy: true,
disableGlobaFilter: true,
header: '',
cell: () => (
<>
<Button size="xs">Edit</Button>
</>
),
size: 100,
},
]
}, [])
return (
<Page height="400px">
<PageHeader title="Customers" />
<PageBody p="0" contentWidth="full" position="relative">
<BulkActions selections={selections} maxW="400px">
<Button colorScheme="white">Delete</Button>
</BulkActions>
<DataGrid
columns={columns}
data={dataTable.data}
isSortable
isSelectable
isHoverable
rowVirtualizer={{ enabled: false }}
onSelectedRowsChange={setSelections}
initialState={{
rowSelection: { 1: true },
}}
>
<DataGridPagination />
</DataGrid>
</PageBody>
</Page>
)
}

Clickable rows#

Cells with a href property will render the cell value in an a. Using the DataGrid onRowClick handler you can trigger a click event on the link whenever the row is clicked.

import { Button } from '@chakra-ui/react'
import {
Page,
PageHeader,
DataGrid,
DataGridPagination,
BulkActions,
} from '@saas-ui-pro/react'
export default function ListPage() {
const [selections, setSelections] = React.useState([])
const columns = React.useMemo(() => {
return [
{
accessorKey: 'name',
header: 'Name',
size: 200,
meta: {
href: ({ id }) => `#customers/${id}`,
},
},
{
accessorKey: 'email',
header: 'Email',
},
{
accessorKey: 'company',
header: 'Company',
},
{
accessorKey: 'country',
header: 'Country',
},
{
accessorKey: 'employees',
header: 'Employees',
meta: {
isNumeric: true,
},
},
{
id: 'action',
disableSortBy: true,
disableGlobaFilter: true,
header: '',
cell: () => (
<>
<Button size="xs">Edit</Button>
</>
),
size: 200,
},
]
}, [])
return (
<Page
height="400px"
sx={{
'& tbody tr:hover': {
cursor: 'pointer',
},
}}
>
<PageHeader title="Customers" />
<PageBody p="0" contentWidth="full" position="relative">
<BulkActions selections={selections} width="400px">
<Button colorScheme="white">Delete</Button>
</BulkActions>
<DataGrid
columns={columns}
data={dataTable.data}
isSortable
isSelectable
isHoverable
onSelectedRowsChange={setSelections}
onRowClick={(row, e) => {
// Find the first A and trigger a click.
const link = e.currentTarget.querySelector('td a')
link && link.click()
}}
>
<DataGridPagination />
</DataGrid>
</PageBody>
</Page>
)
}

Nested rows#

Use the isExpandable prop to enable row expanding. The default property for nested rows is subRows. To use a custom property for nested rows, use the getSubRows prop.

For more than one level of nesting, it's recommended to use tableLayout=auto on the DataGrid component.

import { Button } from '@chakra-ui/react'
import { Page, PageHeader, DataGrid } from '@saas-ui-pro/react'
const demoData = dataTable.data.map((row, i) => {
return {
...row,
subRows: [
{
id: row.id + '-1',
...row,
},
{
id: row.id + '-2',
...row,
},
],
}
})
export default function ListPage() {
const columns = React.useMemo(() => {
return [
{
accessorKey: 'name',
header: 'Name',
size: 200,
meta: {
href: ({ id }) => `#customers/${id}`,
},
},
{
accessorKey: 'email',
header: 'Email',
},
{
accessorKey: 'company',
header: 'Company',
},
{
accessorKey: 'country',
header: 'Country',
},
{
accessorKey: 'employees',
header: 'Employees',
meta: {
isNumeric: true,
},
},
]
}, [])
return (
<Page height="400px">
<PageHeader title="Customers" />
<PageBody p="0" contentWidth="full" position="relative">
<DataGrid columns={columns} data={demoData} isSortable isExpandable />
</PageBody>
</Page>
)
}

Remote data#

By default all sorting, filtering and pagination is handled locally by react-table to work with remote data use this example as a reference.

This example uses React Query to fetch data.

import { Button } from '@chakra-ui/react'
import {
Page,
PageHeader,
DataGrid,
DataGridPagination,
SortingState,
BulkActions,
} from '@saas-ui-pro/react'
// import { useQuery } from '@tanstack/react-query'
export default function ListPage() {
const [selections, setSelections] = React.useState([])
const [sort, setSort] = React.useState < SortingState > []
const [page, setPage] = React.useState(0)
const { data } = useQuery(['customers', sort, page], () => {
// fetch...
})
const columns = React.useMemo(() => {
return [
{
accessorKey: 'name',
header: 'Name',
size: 200,
meta: {
href: ({ id }) => `#customers/${id}`,
},
},
{
accessorKey: 'email',
header: 'Email',
},
{
accessorKey: 'company',
header: 'Company',
},
{
accessorKey: 'country',
header: 'Country',
},
{
accessorKey: 'employees',
header: 'Employees',
meta: {
isNumeric: true,
},
},
{
id: 'action',
disableSortBy: true,
disableGlobaFilter: true,
header: '',
cell: () => (
<>
<Button size="xs">Edit</Button>
</>
),
width: '100px',
},
]
}, [])
return (
<Page
height="400px"
sx={{
'& tbody tr:hover': {
cursor: 'pointer',
},
}}
>
<PageHeader title="Customers" />
<PageBody p="0" contentWidth="full" position="relative">
<BulkActions selections={selections}>
<Button colorScheme="white" variant="subtle">
Delete
</Button>
</BulkActions>
<DataGrid
columns={columns}
data={dataTable.data}
isSortable
isSelectable
isHoverable
onSelectedRowsChange={setSelections}
onRowClick={(row, e) => {
// Find the first A and trigger a click.
const link = e.currentTarget.querySelector('td a')
link && link.click()
}}
manualSortBy
onSortChange={setSort}
pageCount={data.total}
initialState={{
pagination: {
pageSize: 1,
},
}}
state={{
sorting: { sort },
}}
>
<DataGridPagination
onChange={({ pageIndex }) => setPage(pageIndex)}
/>
</DataGrid>
</PageBody>
</Page>
)
}

Access internal state#

You can access the react-table TableInstance by passing a ref to DataGrid. Check out the react-table documentation for all properties.

import {
Page,
PageHeader,
PageBody,
Toolbar,
ToolbarButton,
DataGrid,
} from '@saas-ui-pro/react'
export default function InternalState() {
const gridRef = useRef(null)
return (
<Page title="Customers" height="400px">
<PageHeader
title="Customers"
toolbar={
<Toolbar>
<ToolbarButton
onClick={() => {
gridRef.current.toggleAllRowsSelected()
}}
label="Select all rows"
variant="primary"
/>
</Toolbar>
}
/>
<PageBody p="0" contentWidth="full" position="relative">
<DataGrid
instanceRef={gridRef}
columns={dataGrid.columns.concat()}
data={dataGrid.data.concat()}
isSelectable
/>
</PageBody>
</Page>
)
}

Toggle visible columns#

import {
Menu,
MenuButton,
MenuList,
MenuOptionGroup,
MenuOptionItem,
Button,
} from '@chakra-ui/react'
import {
Page,
PageHeader,
PageBody,
Toolbar,
DataGrid,
useColumnVisibility,
} from '@saas-ui-pro/react'
export default function VisibleColumns() {
const allColumns = ['name', 'email', 'company', 'country', 'employees']
const [visibleColumns, setVisibleColumns] = React.useState([
'name',
'email',
'company',
])
const columnVisibility = useColumnVisibility({
columns: dataGrid.columns,
visibleColumns,
})
return (
<Page title="Customers" height="400px">
<PageHeader
title="Customers"
toolbar={
<Toolbar>
<Menu closeOnSelect={false}>
<MenuButton as={Button}>View</MenuButton>
<MenuList zIndex="dropdown">
<MenuOptionGroup
value={visibleColumns}
type="checkbox"
onChange={setVisibleColumns}
close
>
{allColumns.map((c) => (
<MenuItemOption key={c} value={c}>
{c}
</MenuItemOption>
))}
</MenuOptionGroup>
</MenuList>
</Menu>
</Toolbar>
}
/>
<PageBody p="0" contentWidth="full" position="relative">
<DataGrid
columns={dataGrid.columns.concat()}
data={dataGrid.data.concat()}
isSelectable
state={{
columnVisibility,
}}
/>
</PageBody>
</Page>
)
}

Custom icons#

Icons can be customized by passing a icons map to the DataGrid component.

icondescription
sortAscendingSort ascending
sortDescendingSort descending
rowExpandedExpanded row
rowCollapsedCollapsed row
import {
Menu,
MenuButton,
MenuList,
MenuOptionGroup,
MenuOptionItem,
Button,
} from '@chakra-ui/react'
import {
Page,
PageHeader,
PageBody,
Toolbar,
DataGrid,
} from '@saas-ui-pro/react'
import { FiArrowUp, FiArrowDown, FiArrowRight } from 'react-icons/fi'
const icons = {
sortAscending: <FiArrowUp />,
sortDescending: <FiArrowDown />,
rowExpanded: <FiArrowDown />,
rowCollapsed: <FiArrowRight />,
}
export default function CustomIcons() {
return (
<Page title="Customers" height="400px">
<PageHeader title="Customers" />
<PageBody p="0" contentWidth="full" position="relative">
<DataGrid
columns={dataGrid.columns.concat()}
data={demoData}
isSelectable
isSortable
isExpandable
icons={icons}
/>
</PageBody>
</Page>
)
}
const demoData = dataGrid.data.map((row, i) => {
return {
...row,
subRows: [
{
id: row.id + '-1',
...row,
},
{
id: row.id + '-2',
...row,
},
],
}
})

SlotProps#

Using the slotProps prop you can customize internal components (slots) of the DataGrid component.

import {
Page,
PageHeader,
PageBody,
Toolbar,
ToolbarButton,
DataGrid,
} from '@saas-ui-pro/react'
export default function SlotProps() {
return (
<Page title="Customers" height="400px">
<PageHeader title="Customers" />
<PageBody p="0" contentWidth="full" position="relative">
<DataGrid
columns={dataGrid.columns.concat()}
data={dataGrid.data.concat()}
isSelectable
slotProps={{
row: ({ row, table }) => {
return {
bg: row.original.employees > 100 ? 'red.100' : 'transparent',
_dark: {
bg: row.original.employees > 100 ? 'red.700' : 'transparent',
},
}
},
cell: ({ cell, table }) => {
return {
fontWeight:
cell.column.id === 'employees' ? 'semibold' : 'inherit',
}
},
}}
/>
</PageBody>
</Page>
)
}

Context Menu#

Using slotsProps you can use the as prop to customize the tr element and wrap it with a ContextMenu.

import {
Portal
} from '@chakra-ui/react'
import {
Page,
PageHeader,
PageBody,
Toolbar,
ToolbarButton,
DataGrid,
} from '@saas-ui-pro/react'
import {
ContextMenu,
ContextMenuTrigger,
ContextMenuList,
ContextMenuItem
} from '@saas-ui/react'
export default function DataGridWithContextMenu() {
return (
<Page title="Customers" height="400px">
<PageHeader title="Customers" />
<PageBody p="0" contentWidth="full" position="relative">
<DataGrid
columns={dataGrid.columns.concat()}
data={dataGrid.data.concat()}
slotProps={{
row: () => ({
as: RowWithContext,
}),
}}
/>
</PageBody>
</Page>
)
}
const RowWithContext = React.forwardRef<HTMLTableRowElement, TableRowProps>((props: TableRowProps, ref) => {
return (
<ContextMenu isLazy>
<ContextMenuTrigger as={Tr} ref={ref} {...props}/>
<Portal>
<ContextMenuList>
<MenuItem>Edit</MenuItem>
<MenuItem>Copy</MenuItem>
<MenuItem>Delete</MenuItem>
</ContextMenuList>
</Portal>
</ContextMenu>
)
})

Typescript#

To add typesafety for the meta properties, you need to add react-table-config.d.ts to your src. To use the default DataGrid config you can simply re-export the config from @saas-ui-pro/react.

// Copy to: src/types/react-table-config.d.ts
export * from '@saas-ui-pro/react/src/data-grid/react-table-config.d'
import {
ColumnDef,
DataGrid,
DataGridPagination,
} from '@saas-ui-pro/react'
interface ExampleData {
id: string
name: string
email: string
}
const columns: ColumnDef<ExampleData>[] = [
{
accessorKey: 'id',
header: 'Id',
},
{
accessorKey: 'name',
header: 'Name',
},
{
accessorKey: 'email',
header: 'Email',
},
]
const data: ExampleData[] = [{
{
id: '1',
name: 'TaShya Charles',
email: 'urna.nec.luctus@icloud.couk'
},
{
id: '2',
name: 'Donovan Mosley',
email: 'lacinia.mattis.integer@icloud.couk',
},
}]
<DataGrid<ExampleData> columns={columns} data={data} />

Was this helpful?