Machine coding
Build a Data Grid (Sortable, Filterable, Virtualized)
Difficulty: 🔴 Hard · Est. time:
1h· Tags:#large-data#virtualization#a11y#state
Asked at: Amazon, Atlassian, Stripe, enterprise/dashboard roles · Related: Virtualized List · Kanban
1. The Question
Build a data grid/table that displays thousands of rows with column sorting, filtering, and (bonus) resizable columns and row selection — performant and accessible.
2. Requirements
Functional
Non-functional
3. Component API
type Column<T> = {
key: keyof T;
header: string;
sortable?: boolean;
filterable?: boolean;
width?: number;
render?: (row: T) => React.ReactNode;
};
type DataGridProps<T> = {
columns: Column<T>[];
rows: T[];
rowKey: (row: T) => string;
virtualized?: boolean; // default true when rows > 100
};
4. Implementation Notes & Trade-offs
Derived pipeline → keep raw rows immutable; compute filtered → sorted → windowed via memoized selectors (useMemo). Sorting/filtering the raw data on every render is the common perf mistake — memoize on (rows, sortState, filterState).
Sorting → store { columnKey, direction }. Provide a default comparator (string/number/date) and allow per-column custom comparators. Cycle none→asc→desc. For big data, sort once per change, not per render.
Filtering → per-column predicates combined with global search. Debounce the global search input. For huge datasets, filtering server-side is better — state the trade-off.
Virtualization → windowing is required for thousands of rows; render only visible rows (see virtualized list flagship). Keep the header sticky and outside the scroll window. Fixed row height simplifies the math; variable heights need measurement.
Column resize/reorder → pointer events on the header border for resize (update column width state, throttle with rAF); drag for reorder (reorder the column array).
Accessibility → use real <table>/role="grid" semantics: role="columnheader" with aria-sort, role="row"/role="gridcell". Note the tension: virtualization removes rows from the DOM, so set aria-rowcount/aria-rowindex so assistive tech knows the true size. Keyboard: arrow keys move the focused cell; Enter/Space activate.
Row selection → a Set<rowKey>; header checkbox = select-all (tri-state). Keep selection stable across sort/filter (key by id, not index).
Edge cases → empty state, all-filtered-out, extremely wide tables (horizontal scroll), long cell content (truncate + title), stable sort for equal keys.
5. What Interviewers Probe
- The derived pipeline (filter→sort→window) and memoization.
- Why virtualization is required; sticky header interplay.
- Accessible grid semantics with virtualization (
aria-rowcount). - Stable row identity across sort/filter (key by id).
- Debounced filtering; client vs server sorting/filtering.
- Column resize without re-render storms (rAF).
6. Curated Resources
- Virtualized List flagship ⭐ — the windowing core
- TanStack Table ⭐ — headless grid; study its architecture
- ARIA APG: grid — accessible grid keyboard model
- MDN: table accessibility