High-performance virtualization components for React that render large lists and grids efficiently by only showing visible items
—
Programmatic scrolling controls and DOM access for both List and Grid components through ref-based imperative APIs.
Programmatic control interface for the List component accessible via ref.
interface ListImperativeAPI {
/** Get the outermost DOM element of the list */
get element(): HTMLDivElement | null;
/**
* Scroll to a specific row index with customizable alignment and behavior
* @param params - Scroll configuration
*/
scrollToRow(params: {
/** Zero-based index of the row to scroll to */
index: number;
/** How to align the row within the viewport */
align?: "auto" | "center" | "end" | "smart" | "start";
/** Scroll animation behavior */
behavior?: "auto" | "instant" | "smooth";
}): void;
}Usage Examples:
import React, { useRef } from "react";
import { List, useListRef, useListCallbackRef } from "react-window";
// Using useListRef hook
const ListWithRef = () => {
const listRef = useListRef();
const scrollToRow = (index: number) => {
listRef.current?.scrollToRow({
index,
align: "center",
behavior: "smooth"
});
};
const getListElement = () => {
const element = listRef.current?.element;
if (element) {
console.log("List container dimensions:", element.offsetWidth, element.offsetHeight);
}
};
return (
<div>
<button onClick={() => scrollToRow(500)}>Scroll to Row 500</button>
<button onClick={() => scrollToRow(0)}>Scroll to Top</button>
<button onClick={getListElement}>Log Dimensions</button>
<List
listRef={listRef}
rowComponent={RowComponent}
rowCount={1000}
rowHeight={35}
rowProps={{}}
style={{ height: 400, width: 300 }}
/>
</div>
);
};
// Using useListCallbackRef hook for shared refs
const ListWithCallbackRef = () => {
const [listInstance, setListRef] = useListCallbackRef();
React.useEffect(() => {
if (listInstance) {
// Scroll to middle on mount
listInstance.scrollToRow({
index: 250,
align: "center",
behavior: "instant"
});
}
}, [listInstance]);
return (
<List
listRef={setListRef}
rowComponent={RowComponent}
rowCount={500}
rowHeight={40}
rowProps={{}}
style={{ height: 400, width: 300 }}
/>
);
};Programmatic control interface for the Grid component accessible via ref with support for 2D scrolling.
interface GridImperativeAPI {
/** Get the outermost DOM element of the grid */
get element(): HTMLDivElement | null;
/**
* Scroll to a specific cell with customizable alignment for both dimensions
* @param params - Scroll configuration for both row and column
*/
scrollToCell(params: {
/** Zero-based row index of the cell to scroll to */
rowIndex: number;
/** Zero-based column index of the cell to scroll to */
columnIndex: number;
/** How to align the row within the viewport */
rowAlign?: "auto" | "center" | "end" | "smart" | "start";
/** How to align the column within the viewport */
columnAlign?: "auto" | "center" | "end" | "smart" | "start";
/** Scroll animation behavior */
behavior?: "auto" | "instant" | "smooth";
}): void;
/**
* Scroll to a specific row index
* @param params - Scroll configuration for row
*/
scrollToRow(params: {
/** Zero-based index of the row to scroll to */
index: number;
/** How to align the row within the viewport */
align?: "auto" | "center" | "end" | "smart" | "start";
/** Scroll animation behavior */
behavior?: "auto" | "instant" | "smooth";
}): void;
/**
* Scroll to a specific column index
* @param params - Scroll configuration for column
*/
scrollToColumn(params: {
/** Zero-based index of the column to scroll to */
index: number;
/** How to align the column within the viewport */
align?: "auto" | "center" | "end" | "smart" | "start";
/** Scroll animation behavior */
behavior?: "auto" | "instant" | "smooth";
}): void;
}Usage Examples:
import React, { useRef } from "react";
import { Grid, useGridRef, useGridCallbackRef } from "react-window";
// Using useGridRef hook
const GridWithRef = () => {
const gridRef = useGridRef();
const scrollToCell = (row: number, col: number) => {
gridRef.current?.scrollToCell({
rowIndex: row,
columnIndex: col,
rowAlign: "center",
columnAlign: "center",
behavior: "smooth"
});
};
const scrollToCorners = () => {
// Scroll to different corners with different alignments
setTimeout(() => {
gridRef.current?.scrollToCell({
rowIndex: 0,
columnIndex: 0,
rowAlign: "start",
columnAlign: "start",
behavior: "smooth"
});
}, 0);
setTimeout(() => {
gridRef.current?.scrollToCell({
rowIndex: 99,
columnIndex: 49,
rowAlign: "end",
columnAlign: "end",
behavior: "smooth"
});
}, 2000);
};
const scrollRowOnly = () => {
gridRef.current?.scrollToRow({
index: 25,
align: "center",
behavior: "smooth"
});
};
const scrollColumnOnly = () => {
gridRef.current?.scrollToColumn({
index: 20,
align: "center",
behavior: "smooth"
});
};
return (
<div>
<button onClick={() => scrollToCell(50, 25)}>Scroll to Center</button>
<button onClick={scrollToCorners}>Tour Corners</button>
<button onClick={scrollRowOnly}>Scroll to Row 25</button>
<button onClick={scrollColumnOnly}>Scroll to Column 20</button>
<Grid
gridRef={gridRef}
cellComponent={CellComponent}
cellProps={{}}
columnCount={50}
columnWidth={100}
rowCount={100}
rowHeight={50}
style={{ height: 400, width: 500 }}
/>
</div>
);
};
// Advanced usage with callback ref
const GridWithCallbackRef = () => {
const [gridInstance, setGridRef] = useGridCallbackRef();
const [currentCell, setCurrentCell] = React.useState({ row: 0, col: 0 });
const navigateGrid = (direction: 'up' | 'down' | 'left' | 'right') => {
if (!gridInstance) return;
let newRow = currentCell.row;
let newCol = currentCell.col;
switch (direction) {
case 'up':
newRow = Math.max(0, currentCell.row - 1);
break;
case 'down':
newRow = Math.min(99, currentCell.row + 1);
break;
case 'left':
newCol = Math.max(0, currentCell.col - 1);
break;
case 'right':
newCol = Math.min(49, currentCell.col + 1);
break;
}
setCurrentCell({ row: newRow, col: newCol });
gridInstance.scrollToCell({
rowIndex: newRow,
columnIndex: newCol,
rowAlign: "smart",
columnAlign: "smart",
behavior: "smooth"
});
};
return (
<div>
<div>Current Cell: {currentCell.row}, {currentCell.col}</div>
<div>
<button onClick={() => navigateGrid('up')}>↑</button>
<button onClick={() => navigateGrid('down')}>↓</button>
<button onClick={() => navigateGrid('left')}>←</button>
<button onClick={() => navigateGrid('right')}>→</button>
</div>
<Grid
gridRef={setGridRef}
cellComponent={CellComponent}
cellProps={{ currentCell }}
columnCount={50}
columnWidth={100}
rowCount={100}
rowHeight={50}
style={{ height: 400, width: 500 }}
/>
</div>
);
};The alignment parameter controls how the target item is positioned within the viewport.
type Align = "auto" | "center" | "end" | "smart" | "start";Alignment Behaviors:
"auto": Scroll minimally to bring the item into view"center": Center the item in the viewport"end": Align the item to the end of the viewport (bottom for rows, right for columns)"smart": Minimize scrolling, but center if the item is smaller than the viewport"start": Align the item to the start of the viewport (top for rows, left for columns)The behavior parameter controls the scroll animation using the native DOM ScrollBehavior type.
Scroll Behaviors:
"auto": Use the browser's default scrolling behavior"instant": Jump immediately to the target position without animation"smooth": Animate smoothly to the target positionBoth APIs provide access to the underlying DOM element for advanced use cases.
// Get list container element
const listElement = listRef.current?.element;
if (listElement) {
// Access DOM properties and methods
console.log("Scroll position:", listElement.scrollTop);
console.log("Client dimensions:", listElement.clientWidth, listElement.clientHeight);
// Add event listeners
listElement.addEventListener('scroll', handleScroll);
}
// Get grid container element
const gridElement = gridRef.current?.element;
if (gridElement) {
// Access DOM properties
console.log("Scroll position:", gridElement.scrollTop, gridElement.scrollLeft);
// Measure dimensions
const rect = gridElement.getBoundingClientRect();
console.log("Element bounds:", rect);
}The imperative APIs handle various edge cases gracefully:
const safeScrollToRow = (index: number) => {
if (!listRef.current) {
console.warn("List ref not available");
return;
}
// Clamp index to valid range
const clampedIndex = Math.max(0, Math.min(index, rowCount - 1));
listRef.current.scrollToRow({
index: clampedIndex,
align: "center",
behavior: "smooth"
});
};
const safeScrollToCell = (row: number, col: number) => {
if (!gridRef.current) {
console.warn("Grid ref not available");
return;
}
// Validate indices
if (row < 0 || row >= rowCount || col < 0 || col >= columnCount) {
console.warn("Invalid cell coordinates:", row, col);
return;
}
gridRef.current.scrollToCell({
rowIndex: row,
columnIndex: col,
rowAlign: "smart",
columnAlign: "smart",
behavior: "smooth"
});
};Install with Tessl CLI
npx tessl i tessl/npm-react-window