High-performance virtualization components for React that render large lists and grids efficiently by only showing visible items
—
Convenience hooks for creating properly typed refs for Grid and List components, supporting both regular refs and callback refs with full TypeScript integration.
TypeScript utility hooks for creating properly typed refs for List components.
/**
* Convenience hook to return a properly typed ref for the List component
* @returns RefObject with ListImperativeAPI type
*/
function useListRef(): RefObject<ListImperativeAPI>;
/**
* Convenience hook to return a properly typed callback ref for the List component
* Use this hook when you need to share the ref with another component or hook
* @returns Tuple of [current instance, setter function]
*/
function useListCallbackRef(): [
ListImperativeAPI | null,
(instance: ListImperativeAPI | null) => void
];Usage Examples:
import React from "react";
import { List, useListRef, useListCallbackRef } from "react-window";
// Using useListRef - standard ref pattern
const ListWithStandardRef = () => {
const listRef = useListRef();
const scrollToMiddle = () => {
// TypeScript knows listRef.current is ListImperativeAPI | null
listRef.current?.scrollToRow({
index: 500,
align: "center",
behavior: "smooth"
});
};
return (
<div>
<button onClick={scrollToMiddle}>Scroll to Middle</button>
<List
listRef={listRef} // Type-safe ref assignment
rowComponent={RowComponent}
rowCount={1000}
rowHeight={35}
rowProps={{}}
style={{ height: 400, width: 300 }}
/>
</div>
);
};
// Using useListCallbackRef - callback ref pattern
const ListWithCallbackRef = () => {
const [listInstance, setListRef] = useListCallbackRef();
// Access the instance when it's available
React.useEffect(() => {
if (listInstance) {
// TypeScript knows listInstance is ListImperativeAPI
console.log("List mounted:", listInstance.element);
// Auto-scroll to a specific position
listInstance.scrollToRow({
index: 100,
align: "start",
behavior: "instant"
});
}
}, [listInstance]);
return (
<List
listRef={setListRef} // Type-safe callback ref
rowComponent={RowComponent}
rowCount={1000}
rowHeight={35}
rowProps={{}}
style={{ height: 400, width: 300 }}
/>
);
};
// Sharing refs between components
const SharedListRef = () => {
const [listInstance, setListRef] = useListCallbackRef();
return (
<div>
<ListControls listInstance={listInstance} />
<List
listRef={setListRef}
rowComponent={RowComponent}
rowCount={1000}
rowHeight={35}
rowProps={{}}
style={{ height: 400, width: 300 }}
/>
</div>
);
};
const ListControls = ({ listInstance }: { listInstance: ListImperativeAPI | null }) => {
const scrollToTop = () => {
listInstance?.scrollToRow({ index: 0, align: "start", behavior: "smooth" });
};
const scrollToBottom = () => {
listInstance?.scrollToRow({ index: 999, align: "end", behavior: "smooth" });
};
return (
<div>
<button onClick={scrollToTop} disabled={!listInstance}>
Scroll to Top
</button>
<button onClick={scrollToBottom} disabled={!listInstance}>
Scroll to Bottom
</button>
</div>
);
};TypeScript utility hooks for creating properly typed refs for Grid components.
/**
* Convenience hook to return a properly typed ref for the Grid component
* @returns RefObject with GridImperativeAPI type
*/
function useGridRef(): RefObject<GridImperativeAPI>;
/**
* Convenience hook to return a properly typed callback ref for the Grid component
* Use this hook when you need to share the ref with another component or hook
* @returns Tuple of [current instance, setter function]
*/
function useGridCallbackRef(): [
GridImperativeAPI | null,
(instance: GridImperativeAPI | null) => void
];Usage Examples:
import React from "react";
import { Grid, useGridRef, useGridCallbackRef } from "react-window";
// Using useGridRef - standard ref pattern
const GridWithStandardRef = () => {
const gridRef = useGridRef();
const scrollToCenter = () => {
// TypeScript knows gridRef.current is GridImperativeAPI | null
gridRef.current?.scrollToCell({
rowIndex: 50,
columnIndex: 25,
rowAlign: "center",
columnAlign: "center",
behavior: "smooth"
});
};
const scrollToCorner = () => {
gridRef.current?.scrollToCell({
rowIndex: 0,
columnIndex: 0,
rowAlign: "start",
columnAlign: "start",
behavior: "smooth"
});
};
return (
<div>
<button onClick={scrollToCenter}>Scroll to Center</button>
<button onClick={scrollToCorner}>Scroll to Corner</button>
<Grid
gridRef={gridRef} // Type-safe ref assignment
cellComponent={CellComponent}
cellProps={{}}
columnCount={50}
columnWidth={100}
rowCount={100}
rowHeight={50}
style={{ height: 400, width: 500 }}
/>
</div>
);
};
// Using useGridCallbackRef - callback ref pattern
const GridWithCallbackRef = () => {
const [gridInstance, setGridRef] = useGridCallbackRef();
React.useEffect(() => {
if (gridInstance) {
// TypeScript knows gridInstance is GridImperativeAPI
console.log("Grid mounted:", gridInstance.element);
// Auto-scroll to a specific cell
gridInstance.scrollToCell({
rowIndex: 25,
columnIndex: 12,
rowAlign: "smart",
columnAlign: "smart",
behavior: "instant"
});
}
}, [gridInstance]);
return (
<Grid
gridRef={setGridRef} // Type-safe callback ref
cellComponent={CellComponent}
cellProps={{}}
columnCount={50}
columnWidth={100}
rowCount={100}
rowHeight={50}
style={{ height: 400, width: 500 }}
/>
);
};
// Advanced grid navigation with shared ref
const GridWithNavigation = () => {
const [gridInstance, setGridRef] = useGridCallbackRef();
const [currentPosition, setCurrentPosition] = React.useState({ row: 0, col: 0 });
const navigate = (direction: 'up' | 'down' | 'left' | 'right') => {
if (!gridInstance) return;
const { row, col } = currentPosition;
let newRow = row;
let newCol = col;
switch (direction) {
case 'up':
newRow = Math.max(0, row - 1);
break;
case 'down':
newRow = Math.min(99, row + 1);
break;
case 'left':
newCol = Math.max(0, col - 1);
break;
case 'right':
newCol = Math.min(49, col + 1);
break;
}
setCurrentPosition({ row: newRow, col: newCol });
gridInstance.scrollToCell({
rowIndex: newRow,
columnIndex: newCol,
rowAlign: "smart",
columnAlign: "smart",
behavior: "smooth"
});
};
return (
<div>
<div>Position: {currentPosition.row}, {currentPosition.col}</div>
<GridNavigation onNavigate={navigate} />
<Grid
gridRef={setGridRef}
cellComponent={CellComponent}
cellProps={{ currentPosition }}
columnCount={50}
columnWidth={100}
rowCount={100}
rowHeight={50}
style={{ height: 400, width: 500 }}
/>
</div>
);
};
const GridNavigation = ({ onNavigate }: {
onNavigate: (direction: 'up' | 'down' | 'left' | 'right') => void;
}) => (
<div>
<button onClick={() => onNavigate('up')}>↑</button>
<button onClick={() => onNavigate('down')}>↓</button>
<button onClick={() => onNavigate('left')}>←</button>
<button onClick={() => onNavigate('right')}>→</button>
</div>
);Building custom hooks with the provided utilities for common patterns.
// Custom hook for keyboard navigation in lists
const useListKeyboardNavigation = (
listRef: RefObject<ListImperativeAPI>,
itemCount: number
) => {
const [currentIndex, setCurrentIndex] = React.useState(0);
React.useEffect(() => {
const handleKeyDown = (event: KeyboardEvent) => {
switch (event.key) {
case 'ArrowUp':
event.preventDefault();
const newUpIndex = Math.max(0, currentIndex - 1);
setCurrentIndex(newUpIndex);
listRef.current?.scrollToRow({
index: newUpIndex,
align: "smart",
behavior: "smooth"
});
break;
case 'ArrowDown':
event.preventDefault();
const newDownIndex = Math.min(itemCount - 1, currentIndex + 1);
setCurrentIndex(newDownIndex);
listRef.current?.scrollToRow({
index: newDownIndex,
align: "smart",
behavior: "smooth"
});
break;
case 'Home':
event.preventDefault();
setCurrentIndex(0);
listRef.current?.scrollToRow({
index: 0,
align: "start",
behavior: "smooth"
});
break;
case 'End':
event.preventDefault();
setCurrentIndex(itemCount - 1);
listRef.current?.scrollToRow({
index: itemCount - 1,
align: "end",
behavior: "smooth"
});
break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [currentIndex, itemCount, listRef]);
return currentIndex;
};
// Usage of custom hook
const KeyboardNavigableList = () => {
const listRef = useListRef();
const currentIndex = useListKeyboardNavigation(listRef, 1000);
return (
<div>
<div>Current Item: {currentIndex}</div>
<List
listRef={listRef}
rowComponent={({ index, style }) => (
<div
style={{
...style,
backgroundColor: index === currentIndex ? '#e6f3ff' : 'transparent'
}}
>
Item {index}
</div>
)}
rowCount={1000}
rowHeight={35}
rowProps={{}}
style={{ height: 400, width: 300 }}
/>
</div>
);
};
// Custom hook for grid cell selection
const useGridCellSelection = (
gridRef: RefObject<GridImperativeAPI>,
rowCount: number,
columnCount: number
) => {
const [selectedCell, setSelectedCell] = React.useState<{row: number, col: number} | null>(null);
const selectCell = (row: number, col: number) => {
setSelectedCell({ row, col });
gridRef.current?.scrollToCell({
rowIndex: row,
columnIndex: col,
rowAlign: "smart",
columnAlign: "smart",
behavior: "smooth"
});
};
const clearSelection = () => setSelectedCell(null);
return { selectedCell, selectCell, clearSelection };
};The utility hooks provide several TypeScript benefits:
// Without utility hooks (manual typing required)
const manualListRef = useRef<ListImperativeAPI>(null);
// With utility hook (automatic typing)
const typedListRef = useListRef();
// Both provide the same functionality, but the utility hook
// eliminates the need to import and specify the type manuallyInstall with Tessl CLI
npx tessl i tessl/npm-react-window