Skip to main content

React quick start

import { ColumnType, Grid, type ReactColDef } from "@agility-workbench/react-grid";

const columns: ReactColDef[] = [
{ key: "name", label: "Product" },
{ key: "price", label: "Price", type: ColumnType.CURRENCY, editable: true },
];

export function Products() {
return (
<div style={{ height: 420 }}>
<Grid
rowIdKey="id"
rowData={rows}
columnDefs={columns}
onGridReady={(api) => console.log("Ready", api)}
/>
</div>
);
}

ReactColDef accepts React cell renderers, editors, tooltips, and ActionFrame components. <Grid /> also accepts every core GridOptions field as a prop.

Use ref, apiRef, or onGridReady for imperative API access. onGridReady fires once per grid instance with the columns and row data already applied, so API calls that address columns or rows by id resolve there. Setup and cleanup support React Strict Mode effect replay.

React components in grid slots

function StatusBadge({ value }: CellRendererParams) {
return <span className={`badge badge-${value}`}>{String(value)}</span>;
}

function OwnerTooltip({ data }: TooltipComponentParams) {
return <strong>{data.owner} · {data.ownerEmail}</strong>;
}

function CommentFrame({ value, close }: ActionFrameComponentParams) {
return <button onClick={() => { saveComment(String(value ?? "")); close(); }}>Save comment</button>;
}

const columns: ReactColDef[] = [
{ key: "status", label: "Status", cellRenderer: StatusBadge },
{ key: "owner", label: "Owner", tooltipComponent: OwnerTooltip },
{
key: "comment",
label: "Comment",
actionFrameTrigger: "click",
actionFrameComponent: CommentFrame,
},
];

React components also work in defaultColDef for cell renderers, editors, tooltips, header tooltips, and ActionFrames. Custom header content uses the core DOM component contract.

React cell editor

An editor exposes its value through a ReactCellEditorHandle ref:

const UppercaseEditor = forwardRef<ReactCellEditorHandle, ICellEditorParams>(
function UppercaseEditor({ value }, ref) {
const [draft, setDraft] = useState(String(value ?? ""));
const inputRef = useRef<HTMLInputElement>(null);

useImperativeHandle(ref, () => ({
getValue: () => draft.toUpperCase(),
focus: () => inputRef.current?.focus(),
}), [draft]);

return (
<input
ref={inputRef}
value={draft}
onChange={(event) => setDraft(event.target.value)}
/>
);
},
);

const column: ReactColDef = {
key: "code",
label: "Code",
editable: true,
cellEditor: UppercaseEditor,
};

Event callback props

<Grid
onCellClicked={(event) => console.log(event.rowId, event.colId)}
onRowClicked={(event) => console.log(event.rowId)}
onCellValueChanged={(event) => saveChange(event)}
onSelectionChanged={(event) => console.log(event.snapshot)}
onSortChanged={(event) => console.log(event.changedColIds)}
onFilterChanged={(event) => console.log(event.source, event.changedColIds)}
/>

For other events, subscribe with api.on(eventName, handler).

React-aware menu items and icons

<Grid
icons={{ export: "/icons/download.svg" }}
getColumnMenuItems={({ items }) => [
...items,
{ isSeparator: true },
{ id: "help", label: "Help", left: <HelpIcon />, onClick: openHelp },
]}
bodyContextMenu={({ items }) => [
...items,
{ id: "inspect", label: "Inspect", right: <kbd>I</kbd>, onClick: inspect },
]}
/>

React nodes are accepted in menu left and right slots.