Data grid
A spreadsheet-grade table: sort, resize, freeze columns, and edit cells in place without leaving the row.
See it
What it is
A data grid is a table that behaves like a spreadsheet: sortable and resizable columns, pinned (frozen) header and leading columns, cell selection with keyboard navigation, inline editing, grouping, and copy/paste of ranges. Airtable, Google Sheets, and the Stripe dashboard's transaction view are the mental models. When you go shopping, know which shelf you are on: AG Grid, Handsontable, and Glide Data Grid are batteries-included grids that already own rendering, virtualization, editing, and the keyboard model, while TanStack Table is headless, a state engine for columns, sorting, grouping, pinning, and resizing that renders nothing. Headless is a great foundation and a poor finish line: you still bring your own virtualization (TanStack Virtual or similar), your own cell focus and edit modes, and your own grid semantics.
The line worth drawing: a table displays rows, a data grid lets people work on them. If your users read a list and click through to a detail page, you want a table with a sticky header. If they scan thousands of rows, fix a couple of columns in place, and fix typos without leaving the row, you want a grid.
Gotchas: rendering ten thousand DOM rows kills the page, so real grids virtualize (only the visible window is in the DOM), which then fights with browser find-in-page, sticky positioning, and printing. Accessibility is its own project: role='grid' brings a full keyboard contract (arrows move the cell cursor, Enter edits, Escape cancels) that a div soup will not satisfy. Building one from scratch is a months-long detour, so treat 'we will just make our own grid' as the warning sign it is.
Ask AI for it
Build a data grid over this dataset. Use TanStack Table for column, sorting, pinning, and resizing state, and TanStack Virtual for row virtualization: TanStack Table renders nothing on its own, so do the rendering yourself. Sticky header row, the first column pinned/frozen while the rest scroll horizontally, drag handles on column borders to resize, click-to-sort headers with an ascending/descending caret. Maintain an active cell as {rowId, columnId} with roving tabIndex so exactly one cell is tabbable, move it with the arrow keys, and keep explicit view and edit modes: Enter starts editing on a view-mode cell and commits on an edit-mode cell, Escape cancels back to view mode, and double-click also enters edit mode. Add a visible focus outline on the active cell, zebra-free row separators with a hover highlight, right-aligned numeric columns using tabular-nums, and role='grid' semantics throughout.