Overview
A custom React hook that provides interactive drag-to-resize column functionality for data tables, with sizes persisted to localStorage and minimum widths calculated from header text content.
Description
The useColumnResize hook manages the complete column resizing lifecycle for data tables in the Helicone dashboard. It handles:
- Size initialization -- Column sizes are initialized from column definitions (
minSize or size properties) and persisted to localStorage via the useLocalStorage hook, keyed by a unique tableId. This allows users' column width preferences to survive page reloads and navigation.
- Text measurement -- Uses the Canvas API to measure header text width with the actual header font (
600 12px system-ui). A canvas element is lazily created and reused (via useRef) with the font context configured once and cached via a contextConfigured flag for performance.
- Minimum width calculation --
getMinColumnWidth computes the minimum allowed width as the smaller of the measured header text width plus padding (24px) or the column's default width, with an absolute minimum floor of 60px. This prevents columns from being resized smaller than their header text.
- Mouse drag tracking --
handleResizeStart captures the initial mouse position and column width. Global mousemove and mouseup event listeners (added/removed via useEffect) handle the drag operation, computing the delta and enforcing minimum widths. Event listeners are properly cleaned up to prevent memory leaks.
- Merged sizes -- The returned
columnSizes merges persisted sizes with column definition defaults, ensuring all columns have a width value even if they were not previously resized.
Usage
Use this hook in any table component that needs resizable columns. Pass the column definitions and a unique table identifier. The hook returns the current column sizes, the index of any column being resized (for visual feedback), and a handler to attach to resize grip elements.
Code Reference
Source Location
Signature
interface ColumnWithSize {
minSize?: number;
size?: number;
header?: any;
}
interface UseColumnResizeOptions<T extends ColumnWithSize> {
columns: T[];
tableId: string;
}
interface UseColumnResizeReturn {
columnSizes: Record<number, number>;
resizingColumn: number | null;
handleResizeStart: (columnIndex: number, event: React.MouseEvent) => void;
}
export function useColumnResize<T extends ColumnWithSize>(
options: UseColumnResizeOptions<T>
): UseColumnResizeReturn;
Import
import { useColumnResize } from "@/hooks/useColumnResize";
I/O Contract
Input (Options)
| Field |
Type |
Description
|
columns |
T[] |
Array of column definitions extending ColumnWithSize
|
tableId |
string |
Unique identifier used as the localStorage key prefix
|
Output (Return)
| Field |
Type |
Description
|
columnSizes |
Record<number, number> |
Map of column index to pixel width (merged from storage and defaults)
|
resizingColumn |
null |
Index of the column currently being resized, or null
|
handleResizeStart |
(columnIndex: number, event: React.MouseEvent) => void |
Event handler to attach to column resize grips
|
Constants
| Constant |
Value |
Description
|
COLUMN_PADDING |
24 |
Pixels added to text width for sort icons and spacing
|
MIN_COLUMN_WIDTH |
60 |
Absolute minimum column width in pixels
|
DEFAULT_COLUMN_WIDTH |
120 |
Default width when column definition does not specify
|
HEADER_FONT |
"600 12px -apple-system, ..." |
Font used for Canvas text measurement
|
Usage Examples
import { useColumnResize } from "@/hooks/useColumnResize";
function DataTable({ columns, data }) {
const { columnSizes, resizingColumn, handleResizeStart } = useColumnResize({
columns,
tableId: "requests-table",
});
return (
<table>
<thead>
<tr>
{columns.map((col, idx) => (
<th key={idx} style={{ width: columnSizes[idx] }}>
{col.header}
<div
className={`resize-handle ${resizingColumn === idx ? "active" : ""}`}
onMouseDown={(e) => handleResizeStart(idx, e)}
/>
</th>
))}
</tr>
</thead>
<tbody>
{/* ... render data rows ... */}
</tbody>
</table>
);
}
Related Pages
Page Connections
Double-click a node to navigate. Hold to expand connections.