Skip to content

Grid Component

The <Grid> component is the main entry point for @hobom-grid/react. It handles rendering, virtualization, keyboard navigation, and accessibility automatically.

Basic Usage

tsx
import { Grid } from "@hobom-grid/react";

<Grid
  rowCount={1000}
  colCount={10}
  defaultRowHeight={36}
  defaultColWidth={120}
  headerRowCount={1}
  renderCell={(cell, state) => <div>{`${cell.rowIndex},${cell.colIndex}`}</div>}
/>;

Props

PropTypeDefaultDescription
rowCountnumberrequiredTotal number of rows (including header rows)
colCountnumberrequiredTotal number of columns
defaultRowHeightnumber32Row height estimate used before DOM measurement
defaultColWidthnumber120Column width estimate used before DOM measurement
colSizesRecord<number, number>Pre-set column widths in px, keyed by column index
headerRowCountnumber1Number of rows treated as header (sticky, not scrolled)
pinnedColStartCountnumber0Number of columns pinned to the left
pinnedColEndCountnumber0Number of columns pinned to the right
overscanPxnumber150Extra pixels rendered beyond the visible area (reduces blank-flash on fast scroll)
renderCell(cell, state) => ReactNoderequiredCell renderer function
onCellDoubleClick(row, col) => voidCalled on body cell double-click. Wire to useEditing.gridExtension.onCellDoubleClick
keyboardExtension{ onKeyDown }Called before built-in keyboard handler. Wire to useEditing.gridExtension.keyboardExtension
ariaLabelstring"Data grid"ARIA label for screen readers
styleCSSPropertiesPassed to the root container
classNamestringCSS class on the root container

renderCell

The renderCell function receives two arguments:

cell: CellVM

FieldTypeDescription
rowIndexnumberRow index (0-based, 0 = first header row)
colIndexnumberColumn index (0-based)
xnumberLeft offset in px within the viewport
ynumberTop offset in px within the viewport
widthnumberCell width in px
heightnumberCell height in px
kind"header" | "body" | "pinnedStart" | "pinnedEnd" | "cornerStart" | "cornerEnd"Cell region

state: GridRenderState

FieldTypeDescription
interactionStateInteractionKernelStateCurrent hover/focus/selection/drag state
viewportViewportModelCurrent viewport dimensions and transforms

Keyboard Navigation

When the grid has focus, the following keys are active:

KeyAction
Arrow keysMove focus one cell in that direction
Home / EndMove to first / last column in the row
Ctrl+Home / Ctrl+EndMove to first / last cell in the grid
PageUp / PageDownMove focus by one viewport height
EscapeClear selection

Pinned Columns

tsx
<Grid
  pinnedColStartCount={1}  // first column stays left
  pinnedColEndCount={1}    // last column stays right
  ...
/>

In renderCell, use cell.kind to style pinned vs body columns differently:

tsx
renderCell={(cell) => {
  const isPinned = cell.kind === "pinnedStart" || cell.kind === "pinnedEnd";
  return (
    <div style={{ background: isPinned ? "#f5f5f5" : "white" }}>
      {data[cell.rowIndex][cell.colIndex]}
    </div>
  );
}}

Column Sizing

Pass measured widths via colSizes to control exact column widths. This pairs naturally with useColumnResize:

tsx
const { colWidths, resizeHandlers } = useColumnResize({
  count: columns.length,
  defaultWidth: 120,
});

<Grid colSizes={colWidths} ... />

Accessibility

The grid outputs ARIA attributes automatically:

  • role="grid" on the container
  • role="columnheader" on header cells
  • role="gridcell" on body cells
  • aria-rowindex / aria-colindex on every cell
  • aria-selected on the focused cell
  • aria-rowcount / aria-colcount on the container
  • aria-multiselectable={true} on the container

Use ariaLabel to set a meaningful label for screen readers:

tsx
<Grid ariaLabel="Employee roster" ... />