Skip to content

Getting Started

Add accessible keyboard navigation, selection, and editing to your TanStack Table in minutes.

Installation

bash
bun add @ts-zen/react-datagrid
bash
npm install @ts-zen/react-datagrid
bash
pnpm add @ts-zen/react-datagrid
bash
yarn add @ts-zen/react-datagrid

Quick Start

1. Set up the grid behavior

tsx
import { useGridBehavior } from "@ts-zen/react-datagrid";

function DataGrid({ table }) {
  const grid = useGridBehavior({
    // Pass rows and columns from TanStack Table
    rows: table.getRowModel().rows,
    columns: table.getVisibleLeafColumns(),
  });

  return <div {...grid.props}>{/* Your existing table structure */}</div>;
}

2. Add behavior to cells

tsx
import { useCellBehavior } from "@ts-zen/react-datagrid";

function Cell({ grid, rowId, colId, children }) {
  const cell = useCellBehavior(grid, rowId, colId);

  return (
    <div
      {...cell.props}
      className={cn(cell.isFocused && "ring-2 ring-blue-500", cell.isSelected && "bg-blue-50")}
    >
      {children}
    </div>
  );
}

3. That's it!

You now have:

  • Arrow key navigation between cells
  • Click to focus and select
  • Shift+Click for range selection
  • Ctrl+A to select all
  • Proper ARIA attributes

Adding Features

Editable Cells

tsx
function EditableCell({ grid, rowId, colId, value, onCommit }) {
  const cell = useCellBehavior(grid, rowId, colId, { type: "editable" });

  if (cell.isEditing) {
    return (
      <div {...cell.props}>
        <input
          autoFocus
          defaultValue={value}
          onBlur={(e) => onCommit(e.target.value)}
          onKeyDown={(e) => {
            if (e.key === "Enter") onCommit(e.currentTarget.value);
            if (e.key === "Escape") cell.cancelEdit();
          }}
        />
      </div>
    );
  }

  return (
    <div {...cell.props} className={cell.isFocused && "ring-2"}>
      {value}
    </div>
  );
}

Headers with Interactive Elements

tsx
function Header({ grid, colId, children }) {
  const header = useHeaderBehavior(grid, colId);

  return (
    <div {...header.props} className={header.isFocused && "ring-2"}>
      {children}
      <button {...header.interactive("sort")}>Sort</button>
    </div>
  );
}

Configuration

tsx
const grid = useGridBehavior({
  rows,
  columns,
  config: {
    // Selection mode
    selection: {
      mode: "range", // or 'single'
    },
    // Editing behavior
    editing: {
      commitOnBlur: true,
      startOnType: true, // Start editing when typing
    },
    // Accessibility announcements
    a11y: {
      announceNavigation: true,
      announceSelection: true,
    },
  },
  // Callbacks
  onCommit: ({ cell, value }) => {
    // Save the edited value
  },
  onSelectionChange: ({ ranges }) => {
    // React to selection changes
  },
});

Next Steps

Released under the MIT License.