An editable, composable data grid for the shadcn/ui ecosystem: range selection, spreadsheet clipboard, fill handle, pinning, undo/redo, and streaming updates. Real DOM cells, shadcn styling.
An editable data grid: range selection, spreadsheet clipboard, editing engine, validation, sorting, filtering, pinning, streaming updates. Real DOM cells, shadcn styling.
Action-buttons column pattern: a pinned-right readOnly column whose renderCell composes shadcn Buttons over store actions (startEditing on the row's first editable cell, deleteRows/duplicateRows by view index) plus a more dropdown (Edit, Duplicate, Delete).
A fake saveOrders rejects any committed Quantity over 100 after ~600ms; the onDataChange handler calls actions.setCellErrors to paint the exact cell with the same ring/tint/aria-invalid treatment as a live validate rejection, after commit. A badge reads the count via useDataGridCellErrors(), and a Clear all button calls clearCellErrors().
Every built-in cell type in one grid, plus a read-only column, a validate rule, and a custom renderCell override.
Drag a header to reorder columns within its pin zone (enableColumnReorder, per-column reorderable opt-out, pinned-right column), plus a column picker wired to the programmatic setColumnOrder(id, targetId, position) path.
Column layout persistence: defaultColumnLayout seeds widths/order/pins/hidden from a localStorage snapshot at mount, onColumnLayoutChange writes every resize commit, reorder, pin, and hide back; a Reset button clears the snapshot and re-seeds.
An HR dataset showing all four value-driven styling surfaces: grid-level getRowClassName/getCellClassName plus per-column cellClassName/headerClassName, all using shadcn theme tokens.
Right-click context menus for the gridcn data grid: cut/copy/paste/clear/row operations on cells, sort/pin/hide/autosize on headers.
Right-click cell menu (cut/copy/paste/clear, row operations) and header menu (sort/pin/hide/autosize) via the header dropdown.
Custom context menu items via renderCellMenuItems and renderHeaderMenuItems, replacing the built-in item sets.
Cross-field validation with validateRow: an End time must stay at or after its Start. The row is judged after a whole write gesture commits; a rejected row flags the named cell like a server error (the value still commits, no rollback) and the flag clears once the row is consistent again.
A worked custom cell type: currency storage as a plain number, cached locale-formatted display, and a symbol-stripping fromText, registered via the cellTypes prop.
Column header as a React component: a three-state sort icon (minus / up / down) driven by useDataGridSortState(); header click sorts in sort mode, and a custom header owns its display.
Custom row marker renderers (renderMarker/renderMarkerHeader): a lucide icon marker reflecting the row selection state, and a select-all header button, while the marker cell's own press/drag gestures still select rows.
The hero demo: an editable grid with every built-in cell type, range selection, the free data-grid-fill add-on, and native clipboard, via defaultData with zero state wiring.
Grid with a live inspector panel pretty-printing the latest payload per event: onDataChange, onSelectionChange, onSortChange, onFilterChange/join, onFill, onRowWindowChange, onColumnLayoutChange, and a presence read-back button.
Fill-handle add-on for the gridcn data grid: drag-to-tile with series inference (arithmetic runs, zero-padded numbers, prefix+number sequences), mod+D/mod+R keyboard fill, via the overlay-plugin seam, with zero core re-renders.
A staged dataset with a numeric series (1,2,3), a zero-padded series (001,002), a text-plus-number series (Item 1,2,3), and a plain-text column — dragging the fill handle down each shows series extrapolation vs. plain tiling; Alt/Option forces a copy and Ctrl/Cmd+D/R do the same from the keyboard.
Per-column filter operator overrides via operatorsForColumn and ColumnDef.filterOperators, plus a full column list (including hidden columns) in the sort popover via allColumns.
Undo and redo keep working while focus sits on a custom toolbar - the opt-in useDataGridGlobalShortcuts hook, with the editable-target guard shown live.
Undo/redo add-on for the gridcn data grid: op-based, id-keyed history plus a useDataGridState quick-start wrapper.
Undo/redo via the useDataGridState quick start, with toolbar buttons wired to the returned history object.
A locale Select (English/Deutsch/Arabic) where each locale carries its strings and its text direction: labels, column headers, and hint text translate via deep-merged overrides, and the Arabic locale flips the grid to RTL through the direction prop.
Import/export add-on for the gridcn data grid: xlsx and csv, with delimiter detection and preview. New user-facing strings live under `labels.io` (see lib/labels.ts DataGridIOLabels) — export button/menu, import dialog (file picker, header-row toggle, delimiter select, column mapping, preview, errors).
Import/export toolbar buttons: export downloads the current view as xlsx or csv; import parses a file and replaces the grid's data through onDataChange.
Keyboard shortcuts dialog for the gridcn data grid, generated at runtime from the grid's effective keymap, grouped by category with platform-aware key chips; opens via '?' or a trigger.
A shortcuts dialog generated at runtime from the grid's effective keymap, opened via a toolbar button or the '?' shortcut.
100k generated rows proving row virtualization: only the windowed rows around the viewport ever mount.
Virtual data fetching add-on for the gridcn data grid: useDataGridLazyRows maintains a sparse data array and fetches row windows on demand from onRowWindowChange, with range coalescing, in-flight dedup, abort-on-unmount, and error revert/refetch. A dev-only DataGridLazyGuard warns when uncontrolled sort/filter/search fires while rows are still unloaded.
100k rows, only a handful loaded up front; a simulated-latency in-memory API fetches each scrolled-into window on demand, with skeleton rows filling in as data lands.
10k rows fetched through a TanStack Query cache: each scrolled window becomes a fetchQuery (deduped, cached for the session, scrolling back never refetches).
10k rows fetched through SWR's global cache via preload: each scrolled window is fetched once per key and shared with any useSWR consumer.
Two Switches (loading, rows present) reach all three states of the loading prop: zero rows + loading = viewport-filling skeleton, rows present + loading = a slim indeterminate bar under the header, zero rows + not loading = the ordinary empty state.
The quick-start minimum: the core item alone, `defaultData`, zero app-side state.
Pagination add-on for the gridcn data grid: useDataGridPagination (client mode slices data; server mode is fully controlled) plus a composable <DataGridPaginationBar /> footer (range, page-size select, first/prev/pages/next/last parts) — shadcn-Pagination style, renders a default layout with no children or a custom composition of parts. New user-facing strings live under labels.pagination (see data-grid/labels.ts DataGridPaginationLabels). Zero core changes — purely additive.
Client-mode pagination: useDataGridPagination slices a generated dataset and <DataGridPaginationBar /> renders the default footer (first/prev/pages/next/last, page-size select, range label).
Server-mode pagination backed by useQuery: the current page/pageSize is the query key, edits write through to the fake server then invalidate the cached pages.
Server-mode pagination backed by useSWR: the page is cached by key so paging back is instant, and edits revalidate the current key after writing through.
100k generated rows with a top-left FPS meter overlay so the scroll-performance claim is checkable live.
Pinned top/bottom row bands add-on for the gridcn data grid: useDataGridPinnedRows registers a rowBands spec (e.g. a totals row) that stays sticky under the header or at the viewport's bottom edge, pixel-aligned with data rows and pinned columns. useDataGridAggregate computes filter-aware sum/avg/min/max/count for those bands.
Totals and averages rows pinned under the header, recomputed live from the grid's data on every edit, and filter-aware via a toolbar filter menu on the Role column.
All four pin zones at once: Name pinned left, Score pinned right, plus an averages row pinned to the top band and a totals row pinned to the bottom band via the data-grid-pinned-rows add-on, over 200 rows so both directions scroll.
Column pinning (left/right), resize (incl. a resizable:false column and a flex:1 long-content column for double-click autosize), and drag-to-reorder, with the header dropdown menu for pin/unpin/autosize and a toolbar columns menu for show/hide.
Every add-on composed in one grid: toolbar, context menu, fill, undo/redo, keybindings, sort list, import/export, plus independent toggles (row markers, pinned totals, validation, read-only, loading, RTL, presence, streaming, density, header click) and an exclusive virtualized/paginated/lazy mode select.
Multiplayer presence add-on for the gridcn data grid: useDataGridPresence registers an overlay plugin that paints remote users' live selections as named, colored highlights, with zero core re-renders.
3 simulated users move cell/range selections on a timer via useDataGridPresence's setPresenceHighlights (2 in view-space range form, 1 rowId-native), with toolbar avatar chips, one user periodically overlapping another, and headerClickBehavior="sort" proving the rowId-native highlight follows its row through a sort.
rowMarkers modes (none/number/checkbox/both): a pinned-left marker column outside the data index space, switchable at runtime.
Row operations from two surfaces: toolbar buttons (insert above/below, duplicate, delete on the current selection) and the right-click context menu, with a side panel showing the last onDataChange payload (source + id-keyed ops) and a read-only toggle.