CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-window

High-performance virtualization components for React that render large lists and grids efficiently by only showing visible items

Pending
Overview
Eval results
Files

grid-virtualization.mddocs/

Grid Virtualization

Two-dimensional virtualized scrolling component for efficiently rendering large grids of data by only rendering visible cells in the viewport.

Capabilities

Grid Component

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 }}
    />
  );
};

Cell Component Requirements

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:

  • The style prop must be applied to the root element of your cell component
  • The style contains positioning (top, left) and sizing (height, width) properties
  • Do not override positioning properties in the style prop
  • Use rowIndex and columnIndex to access your data or determine cell content

Grid Imperative API

Programmatic 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>
  );
};

Event Callbacks

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 }}
    />
  );
};

RTL (Right-to-Left) Support

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

docs

grid-virtualization.md

imperative-apis.md

index.md

list-virtualization.md

typescript-utilities.md

tile.json