Data grid

Sorts, selects and pages records from data.

Rows

  • Runs12
    run_8f28supportglaze-large41.0s38.9kFailed
    run_8f20refund-agentglaze-large9.0s22.1kFailed
    run_8f25refund-agentglaze-small18.4s12.8kWarning
    run_8f26searchglaze-large0.6s1.0kRunning
    run_8f22billingglaze-largeNone0.0kQueued

    12 rows, sorted by Status worst first, page 1 of 3

    pageddefault

  • Runs12
    run_8f2arefund-agentglaze-large1.2s4.8kPassed
    run_8f29triageglaze-small0.9s1.2kPassed
    run_8f28supportglaze-large41.0s38.9kFailed
    run_8f27billingglaze-small2.2s2.4kPassed
    run_8f26searchglaze-large0.6s1.0kRunning
    run_8f25refund-agentglaze-small18.4s12.8kWarning
    run_8f24triageglaze-large3.3s5.1kPassed
    run_8f23supportglaze-small0.9s0.9kPassed
    run_8f22billingglaze-largeNone0.0kQueued
    run_8f21searchglaze-small1.5s1.7kPassed
    run_8f20refund-agentglaze-large9.0s22.1kFailed
    run_8f1ftriageglaze-small0.8s0.6kPassed

    12 rows

    scrolled

States

  • run_8f2arefund-agentglaze-large1.2s4.8kPassed
    run_8f29triageglaze-small0.9s1.2kPassed
    run_8f28supportglaze-large41.0s38.9kFailed
    run_8f27billingglaze-small2.2s2.4kPassed

    4 rows

    rest

  • run_8f28supportglaze-large41.0s38.9kFailed
    run_8f2arefund-agentglaze-large1.2s4.8kPassed
    run_8f29triageglaze-small0.9s1.2kPassed
    run_8f27billingglaze-small2.2s2.4kPassed

    4 rows, sorted by Status worst first

    sorted

  • run_8f2arefund-agentglaze-large1.2s4.8kPassed
    run_8f29triageglaze-small0.9s1.2kPassed
    run_8f28supportglaze-large41.0s38.9kFailed
    run_8f27billingglaze-small2.2s2.4kPassed

    4 rows, 2 selected

    selected

  • run_8f2arefund-agentglaze-large1.2s4.8kPassed
    run_8f29triageglaze-small0.9s1.2kPassed
    run_8f28supportglaze-large41.0s38.9kFailed
    run_8f27billingglaze-small2.2s2.4kPassed

    Loading rows

    loading

  • No runs match timeout

    0 rows

    empty

Usage

When to use it
Use
Records from data that a person sorts, picks in bulk, pages through or trims to the columns they need.
Use
A run, key or deploy list on a phone, where the less important columns drop out by width.
Use
A long list in a fixed height: maxHeight sticks the heads, and rows past virtualizeFrom render only in view.
Not for
A short list written by hand, with no sorting or paging. Use Table.
Not for
Each record is one line of mixed content. Use Item.

Rules

9 rules
Must
Pass getRowId from a stable id, and rowName to name each row's checkbox. Because TanStack keys rows by index without it, so a selection lands on other rows after a sort or a new page of data, and "Select row 3" says nothing.
Must
Set meta.severity on a status column, and open with defaultSorting={[{id: 'status', desc: true}]} when the list is about what broke. Because sorted by the alphabet, Failed lands between Done and Passed, where a person scanning for trouble misses it.
Must
Give a column of durations or counts meta.unit and raw numbers, never preformatted strings. Because the grid picks one unit from the column's largest value and sorts by the number, while strings sort "9.0s" after "41.0s" and mix units.
Should
Give every column but the name, the main number and the status a meta.hideBelow, widest first to go. Because a phone then keeps the columns that answer the question, and the columns menu brings the rest back on demand.
Must
Set meta.mono on an ID, key or path column; never render a <code> or font-mono in the cell. Because meta.mono sets type-code-sm, 0.8 of the cells, so the wider Glaze Mono does not read a size bigger than its neighbours.
Must
Page rows the grid holds with pageSize and noun; for rows paged by the server, pass footer with PaginationInfo and PaginationNav size="sm". Because the range and the links sit on the shell's footer row, and a hand-made pager above the grid splits the record count from the records.
Never
Wrap a DataGrid in a Card or a bordered box; it brings the shell and panel, or takes frame="none" inside a Card's flush content. Because a card around the grid's card is two edges where one closes the group.
Should
Act on the selected rows with a BulkActionBar fed by rowSelection, above the grid or floating, never with buttons in each row. Because the bar names the count and clears with Escape, and per-row buttons repeat the same action down the page.
Should
Open a row with onRowClick and show which is open with activeRowId, apart from selection. Because the rows become one tab stop with arrow keys, and the open row takes aria-current without ticking a checkbox.

State order

6 states, highest first
1
loading
2
empty
3
selected
4
open
5
hover
6
rest

Accessibility

5 checks
The table carries aria-label, aria-busy while loading and aria-rowcount for every row, paged or not.
Each head is a sort button named "Duration, sorted descending. Sort", with aria-sort on the th; its menu button is named for the column.
Checkboxes are named by rowName; the head one is "Select all rows" and shows mixed for some.
A polite status line says the row count, the selection, the sort and the page after each change.
Resize handles are separators with aria-valuenow, moved by the arrow keys; hiding a column moves focus to its neighbour.

Keyboard

4 keys
Shift + click
On a head, adds that column to the sort; on a checkbox, ticks or clears the range from the last one pressed.
Arrow Left / Right
On a resize handle, narrows or widens the column by 16px, 64px with Shift; Enter resets it.
Arrow Up / Down, Home, End, Page Up / Down
With onRowClick, moves between rows.
Enter
On a row with onRowClick, opens it.

Edge cases

6 edge cases
Missing number
A unit column shows None and leaves it out of the unit choice; it sorts last.
Narrow grid
Columns with hideBelow wider than the grid hide; one turned on in the columns menu stays.
Reload
The rows stay until the placeholders are due, so a fast refetch does not flash.
Sort while paged
Goes back to page 1; the selection is kept across pages.
Head label longer than the column
The column grows to fit its head, so no label is cut.
Cut value
The cell ends in an ellipsis and shows the whole value on hover.

API

17 props
columns
ColumnDef<TData>[]
data
TData[]
getRowId
(row, index) => string
rowName
(row) => string
title, count, actions
ReactNode
toolbar
(table) => ReactNode
pageSize, noun
number, string
footer
ReactNode
loading, loadingRows
boolean, number default false, 5
empty
ReactNode default 'No rows'
maxHeight, virtualizeFrom
number | string, number default , 200
sorting, defaultSorting, onSortingChange
SortingState default []
rowSelection, onRowSelectionChange
RowSelectionState default {}
columnVisibility, defaultColumnVisibility
VisibilityState default {}
onRowClick, activeRowId
(row) => void, string
rowActions, rowIndicator
(row) => ReactNode
frame
'card' | 'none' default 'card'

Tokens

18 tokens
--shell
#f1f1f0
--paper
#ffffff
--line-faint
rgb(5 5 5 / 0.06)
--line
rgb(5 5 5 / 0.10)
--hover
rgb(5 5 5 / 0.06)
--pressed
rgb(5 5 5 / 0.10)
--field
#efefef
--row
36px
--control-sm
26px
--text-1
rgb(5 5 5 / 0.80)
--text-2
rgb(5 5 5 / 0.60)
--text-3
rgb(5 5 5 / 0.45)
--type-body-sm
400 13px/1.5 var(--font-sans)
--type-label-sm
500 12px/1.35 var(--font-sans)
--type-code-sm
400 calc(13px * var(--mono-scale))/1.5 var(--font-mono)
--wait-show
150ms
--wait-hold
400ms
--z-raised
10

Install

For your coding agent

GitHub

Please star me pookie

141

A new design system every week

Leave your email before you copy the prompt.