High-performance virtualization components for React that render large lists and grids efficiently by only showing visible items
—
Two-dimensional virtualized scrolling component for efficiently rendering large grids of data by only rendering visible cells in the viewport.
Creates a virtualized grid that renders only visible cells for optimal performance with large 2D datasets.
/**
* Virtualized grid component for efficiently rendering large 2D datasets
* @param props - Grid configuration and rendering props
* @returns Rendered grid component
*/
function Grid<CellProps extends object>(props: GridProps<CellProps>): JSX.Element;
interface GridProps<CellProps extends object> {
/** React component responsible for rendering a cell */
cellComponent: (props: { columnIndex: number; rowIndex: number; style: CSSProperties } & CellProps) => ReactNode;
/** Additional props passed to the cell-rendering component */
cellProps: CellProps;
/** Number of columns to be rendered in the grid */
columnCount: number;
/** Column width in pixels, percentage, or function that returns width */
columnWidth: number | string | ((index: number, cellProps: CellProps) => number);
/** Number of rows to be rendered in the grid */
rowCount: number;
/** Row height in pixels, percentage, or function that returns height */
rowHeight: number | string | ((index: number, cellProps: CellProps) => number);
/** Ref for accessing imperative API methods */
gridRef?: Ref<GridImperativeAPI>;
/** Callback when visible cell range changes */
onCellsRendered?: (args: { columnStartIndex: number; columnStopIndex: number; rowStartIndex: number; rowStopIndex: number }) => void;
/** Callback when grid container resizes */
onResize?: (size: { height: number; width: number }, prevSize: { height: number; width: number }) => void;
/** Additional cells to render outside visible area (default: 3) */
overscanCount?: number;
/** CSS class name for the grid container */
className?: string;
/** CSS properties for the grid container */
style?: CSSProperties;
/** Text direction for RTL support */
dir?: "ltr" | "rtl";
/** Default height for server-side rendering */
defaultHeight?: number;
/** Default width for server-side rendering */
defaultWidth?: number;
}Usage Examples:
import React from "react";
import { Grid } from "react-window";
// Basic grid with fixed cell sizes
const BasicGrid = () => {
const CellComponent = ({
columnIndex,
rowIndex,
style
}: {
columnIndex: number;
rowIndex: number;
style: React.CSSProperties;
}) => (
<div
style={{
...style,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid #ddd'
}}
>
{rowIndex},{columnIndex}
</div>
);
return (
<Grid
cellComponent={CellComponent}
cellProps={{}}
columnCount={100}
columnWidth={100}
rowCount={100}
rowHeight={50}
style={{ height: 400, width: 500 }}
/>
);
};
// Grid with custom cell props and data
interface CellData {
data: string[][];
onCellClick: (row: number, col: number) => void;
selectedCell: { row: number; col: number } | null;
}
const DataGrid = () => {
const [selectedCell, setSelectedCell] = React.useState<{ row: number; col: number } | null>(null);
// Generate sample data
const data = Array.from({ length: 100 }, (_, row) =>
Array.from({ length: 50 }, (_, col) => `Data ${row}-${col}`)
);
const CellComponent = ({
columnIndex,
rowIndex,
style,
data,
onCellClick,
selectedCell
}: {
columnIndex: number;
rowIndex: number;
style: React.CSSProperties;
} & CellData) => {
const isSelected = selectedCell?.row === rowIndex && selectedCell?.col === columnIndex;
return (
<div
style={{
...style,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid #ddd',
backgroundColor: isSelected ? '#e6f3ff' : 'white',
cursor: 'pointer'
}}
onClick={() => onCellClick(rowIndex, columnIndex)}
>
{data[rowIndex][columnIndex]}
</div>
);
};
return (
<Grid
cellComponent={CellComponent}
cellProps={{
data,
onCellClick: (row: number, col: number) => setSelectedCell({ row, col }),
selectedCell
}}
columnCount={50}
columnWidth={120}
rowCount={100}
rowHeight={40}
style={{ height: 400, width: 600 }}
/>
);
};
// Grid with variable cell sizes
const VariableSizeGrid = () => {
const getColumnWidth = (index: number) => {
// Vary column widths
return index % 3 === 0 ? 150 : index % 3 === 1 ? 100 : 200;
};
const getRowHeight = (index: number) => {
// Vary row heights
return index % 2 === 0 ? 50 : 80;
};
const CellComponent = ({
columnIndex,
rowIndex,
style
}: {
columnIndex: number;
rowIndex: number;
style: React.CSSProperties;
}) => (
<div
style={{
...style,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '1px solid #ddd',
backgroundColor: (rowIndex + columnIndex) % 2 === 0 ? '#f5f5f5' : 'white'
}}
>
{rowIndex},{columnIndex}
</div>
);
return (
<Grid
cellComponent={CellComponent}
cellProps={{}}
columnCount={50}
columnWidth={getColumnWidth}
rowCount={100}
rowHeight={getRowHeight}
style={{ height: 400, width: 600 }}
/>
);
};The component passed to cellComponent must accept specific props and handle styling correctly.
interface CellComponentProps<CellProps extends object = object> {
/** Zero-based column index of the cell being rendered */
columnIndex: number;
/** Zero-based row index of the cell being rendered */
rowIndex: number;
/** CSS properties for positioning and sizing the cell */
style: CSSProperties;
}
type CellComponent<CellProps extends object> = (
props: CellComponentProps & CellProps
) => ReactNode;Important Notes:
style prop must be applied to the root element of your cell componentstyle contains positioning (top, left) and sizing (height, width) propertiesrowIndex and columnIndex to access your data or determine cell contentProgrammatic control interface for the Grid component accessible via ref.
interface GridImperativeAPI {
/** Get the outermost DOM element of the grid */
get element(): HTMLDivElement | null;
/**
* Scroll to a specific cell
* @param params - Scroll configuration for both row and column
*/
scrollToCell(params: {
rowIndex: number;
columnIndex: number;
rowAlign?: "auto" | "center" | "end" | "smart" | "start";
columnAlign?: "auto" | "center" | "end" | "smart" | "start";
behavior?: "auto" | "instant" | "smooth";
}): void;
/**
* Scroll to a specific row
* @param params - Scroll configuration for row
*/
scrollToRow(params: {
index: number;
align?: "auto" | "center" | "end" | "smart" | "start";
behavior?: "auto" | "instant" | "smooth";
}): void;
/**
* Scroll to a specific column
* @param params - Scroll configuration for column
*/
scrollToColumn(params: {
index: number;
align?: "auto" | "center" | "end" | "smart" | "start";
behavior?: "auto" | "instant" | "smooth";
}): void;
}Usage Example:
import React, { useRef } from "react";
import { Grid, useGridRef } from "react-window";
const ScrollableGrid = () => {
const gridRef = useGridRef();
const scrollToCenter = () => {
gridRef.current?.scrollToCell({
rowIndex: 50,
columnIndex: 25,
rowAlign: "center",
columnAlign: "center",
behavior: "smooth"
});
};
const scrollToTopLeft = () => {
gridRef.current?.scrollToCell({
rowIndex: 0,
columnIndex: 0,
rowAlign: "start",
columnAlign: "start",
behavior: "smooth"
});
};
const scrollToLastColumn = () => {
gridRef.current?.scrollToColumn({
index: 49,
align: "end",
behavior: "smooth"
});
};
return (
<div>
<button onClick={scrollToCenter}>Scroll to Center</button>
<button onClick={scrollToTopLeft}>Scroll to Top-Left</button>
<button onClick={scrollToLastColumn}>Scroll to Last Column</button>
<Grid
gridRef={gridRef}
cellComponent={CellComponent}
cellProps={{}}
columnCount={50}
columnWidth={100}
rowCount={100}
rowHeight={50}
style={{ height: 400, width: 500 }}
/>
</div>
);
};The Grid component provides callbacks for monitoring visibility changes and resize events.
type OnCellsRendered = (args: {
columnStartIndex: number;
columnStopIndex: number;
rowStartIndex: number;
rowStopIndex: number;
}) => void;
type OnResize = (
size: { height: number; width: number },
prevSize: { height: number; width: number }
) => void;Usage Examples:
const MonitoredGrid = () => {
const handleCellsRendered = ({
columnStartIndex,
columnStopIndex,
rowStartIndex,
rowStopIndex
}: {
columnStartIndex: number;
columnStopIndex: number;
rowStartIndex: number;
rowStopIndex: number;
}) => {
console.log(`Rendering cells from (${rowStartIndex},${columnStartIndex}) to (${rowStopIndex},${columnStopIndex})`);
// Load more data when approaching edges
if (rowStopIndex > totalRows - 10 || columnStopIndex > totalColumns - 10) {
loadMoreData();
}
};
const handleResize = (size: { height: number; width: number }) => {
console.log(`Grid resized to ${size.width}x${size.height}`);
};
return (
<Grid
cellComponent={CellComponent}
cellProps={{}}
columnCount={100}
columnWidth={100}
rowCount={100}
rowHeight={50}
onCellsRendered={handleCellsRendered}
onResize={handleResize}
style={{ height: 400, width: 500 }}
/>
);
};Grid supports right-to-left languages through the dir prop.
// RTL Grid example
const RTLGrid = () => {
return (
<Grid
cellComponent={CellComponent}
cellProps={{}}
columnCount={20}
columnWidth={100}
rowCount={50}
rowHeight={50}
dir="rtl" // Enable RTL support
style={{ height: 400, width: 500 }}
/>
);
};Install with Tessl CLI
npx tessl i tessl/npm-react-window