React components for efficiently rendering large lists and tabular data
—
Advanced components for complex layout requirements including arbitrary positioning, masonry layouts, and multi-grid arrangements.
Renders arbitrarily positioned and sized cells efficiently, perfect for complex layouts where items don't follow a regular grid pattern.
/**
* Renders arbitrarily positioned cells efficiently
* @param props - Collection configuration
*/
function Collection(props: {
/** Aria label for accessibility */
'aria-label'?: string;
/** Total number of cells */
cellCount: number;
/** Cell rendering function */
cellRenderer: (params: {index: number, key: string, style: object}) => React.Node;
/** Function that returns position and size for each cell */
cellSizeAndPositionGetter: (params: {index: number}) => {
height: number,
width: number,
x: number,
y: number
};
/** Optional CSS class name */
className?: string;
/** Height constraint for collection */
height: number;
/** Horizontal offset for scrolling */
horizontalOverscanSize?: number;
/** Renderer when no content is present */
noContentRenderer?: () => React.Node;
/** Callback when section is rendered */
onSectionRendered?: (params: {indices: number[]}) => void;
/** Scroll event callback */
onScroll?: (params: {clientHeight: number, clientWidth: number, scrollHeight: number, scrollLeft: number, scrollTop: number, scrollWidth: number}) => void;
/** Horizontal scroll offset */
scrollLeft?: number;
/** Vertical scroll offset */
scrollTop?: number;
/** Inline styles */
style?: object;
/** Vertical offset for scrolling */
verticalOverscanSize?: number;
/** Width constraint for collection */
width: number;
}): React.Component;Usage Examples:
import React from 'react';
import { Collection, AutoSizer } from 'react-virtualized';
// Basic Collection with random positioning
function RandomCollection({ items }) {
const cellSizeAndPositionGetter = ({ index }) => {
const item = items[index];
return {
height: item.height,
width: item.width,
x: item.x,
y: item.y
};
};
const cellRenderer = ({ index, key, style }) => (
<div key={key} style={style} className="collection-cell">
<div className="cell-content">
<h4>{items[index].title}</h4>
<p>{items[index].description}</p>
</div>
</div>
);
return (
<div style={{ height: 600, width: 800 }}>
<Collection
cellCount={items.length}
cellRenderer={cellRenderer}
cellSizeAndPositionGetter={cellSizeAndPositionGetter}
height={600}
width={800}
/>
</div>
);
}
// Interactive Collection with drag-and-drop positioning
function InteractiveCollection({ items, onItemMove }) {
const [positions, setPositions] = useState(
items.reduce((acc, item, index) => {
acc[index] = { x: item.x, y: item.y };
return acc;
}, {})
);
const cellSizeAndPositionGetter = ({ index }) => {
const position = positions[index];
return {
height: 120,
width: 200,
x: position.x,
y: position.y
};
};
const cellRenderer = ({ index, key, style }) => {
const handleMouseDown = (e) => {
// Implement drag logic here
const startX = e.clientX - positions[index].x;
const startY = e.clientY - positions[index].y;
const handleMouseMove = (e) => {
const newX = e.clientX - startX;
const newY = e.clientY - startY;
setPositions(prev => ({
...prev,
[index]: { x: newX, y: newY }
}));
};
const handleMouseUp = () => {
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
onItemMove?.(index, positions[index]);
};
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
};
return (
<div
key={key}
style={{
...style,
cursor: 'move',
border: '2px solid #ddd',
borderRadius: '4px',
backgroundColor: 'white'
}}
onMouseDown={handleMouseDown}
className="draggable-cell"
>
<div style={{ padding: 10 }}>
<h4>{items[index].title}</h4>
<p>{items[index].content}</p>
</div>
</div>
);
};
return (
<div style={{ height: 600, width: 800, position: 'relative' }}>
<Collection
cellCount={items.length}
cellRenderer={cellRenderer}
cellSizeAndPositionGetter={cellSizeAndPositionGetter}
height={600}
width={800}
/>
</div>
);
}Renders dynamic, Pinterest-style masonry layouts where items flow into columns based on their heights.
/**
* Renders Pinterest-style masonry layout
* @param props - Masonry configuration
*/
function Masonry(props: {
/** Total number of cells */
cellCount: number;
/** Cache for measuring cell dimensions */
cellMeasurerCache?: CellMeasurerCache;
/** Position manager for cell placement */
cellPositioner: object;
/** Cell rendering function */
cellRenderer: (params: {index: number, key: string, parent: object, style: object}) => React.Node;
/** Optional CSS class name */
className?: string;
/** Height constraint for masonry */
height: number;
/** Optional id attribute */
id?: string;
/** Callback when cells are rendered */
onCellsRendered?: (params: {startIndex: number, stopIndex: number}) => void;
/** Scroll event callback */
onScroll?: (params: {clientHeight: number, scrollHeight: number, scrollTop: number}) => void;
/** Number of extra cells to render */
overscanByPixels?: number;
/** Callback when scroll position changes */
onScrollToChange?: (params: {align: string, scrollTop: number}) => void;
/** Column index to scroll to */
scrollToIndex?: number;
/** Scroll alignment behavior */
scrollToAlignment?: 'auto' | 'end' | 'start' | 'center';
/** Vertical scroll offset */
scrollTop?: number;
/** Inline styles */
style?: object;
/** Tab index for focus */
tabIndex?: number;
/** Width constraint for masonry */
width: number;
}): React.Component;Creates a position manager for masonry layouts, handling the column-based positioning logic.
/**
* Creates a cell positioner for masonry layouts
* @param params - Positioner configuration
*/
function createMasonryCellPositioner(params: {
/** Cache for cell measurements */
cellMeasurerCache: CellMeasurerCache;
/** Number of columns in the masonry */
columnCount: number;
/** Width of each column */
columnWidth: number;
/** Space between items */
spacer?: number;
}): {
/** Reset the positioner state */
reset: (params: {columnCount: number, columnWidth: number, spacer?: number}) => void;
};Usage Examples:
import React, { useMemo } from 'react';
import {
Masonry,
CellMeasurer,
CellMeasurerCache,
createMasonryCellPositioner,
AutoSizer
} from 'react-virtualized';
// Basic masonry layout
function BasicMasonry({ items }) {
const cache = useMemo(() => new CellMeasurerCache({
defaultHeight: 200,
fixedWidth: true
}), []);
const cellPositioner = useMemo(() =>
createMasonryCellPositioner({
cellMeasurerCache: cache,
columnCount: 3,
columnWidth: 200,
spacer: 10
}), [cache]
);
const cellRenderer = ({ index, key, parent, style }) => (
<CellMeasurer
cache={cache}
index={index}
key={key}
parent={parent}
>
<div style={style} className="masonry-item">
<img
src={items[index].imageUrl}
alt={items[index].title}
style={{ width: '100%', height: 'auto' }}
/>
<div className="item-content">
<h3>{items[index].title}</h3>
<p>{items[index].description}</p>
</div>
</div>
</CellMeasurer>
);
return (
<div style={{ height: 600, width: 630 }}>
<Masonry
cellCount={items.length}
cellMeasurerCache={cache}
cellPositioner={cellPositioner}
cellRenderer={cellRenderer}
height={600}
width={630}
/>
</div>
);
}
// Responsive masonry with dynamic columns
function ResponsiveMasonry({ items }) {
const [columnCount, setColumnCount] = useState(3);
const cache = useMemo(() => new CellMeasurerCache({
defaultHeight: 250,
fixedWidth: true
}), []);
const cellPositioner = useMemo(() => {
const columnWidth = Math.floor((800 - (columnCount + 1) * 10) / columnCount);
return createMasonryCellPositioner({
cellMeasurerCache: cache,
columnCount,
columnWidth,
spacer: 10
});
}, [cache, columnCount]);
// Reset positioner when column count changes
useEffect(() => {
const columnWidth = Math.floor((800 - (columnCount + 1) * 10) / columnCount);
cellPositioner.reset({
columnCount,
columnWidth,
spacer: 10
});
}, [cellPositioner, columnCount]);
const cellRenderer = ({ index, key, parent, style }) => {
const item = items[index];
return (
<CellMeasurer
cache={cache}
index={index}
key={key}
parent={parent}
>
{({ measure, registerChild }) => (
<div
ref={registerChild}
style={{
...style,
borderRadius: '8px',
overflow: 'hidden',
backgroundColor: 'white',
boxShadow: '0 2px 8px rgba(0,0,0,0.1)'
}}
className="responsive-masonry-item"
>
<img
src={item.imageUrl}
alt={item.title}
onLoad={measure}
style={{ width: '100%', height: 'auto', display: 'block' }}
/>
<div style={{ padding: 12 }}>
<h4 style={{ margin: '0 0 8px 0' }}>{item.title}</h4>
<p style={{ margin: 0, color: '#666' }}>{item.description}</p>
{item.tags && (
<div style={{ marginTop: 8 }}>
{item.tags.map(tag => (
<span key={tag} className="tag">{tag}</span>
))}
</div>
)}
</div>
</div>
)}
</CellMeasurer>
);
};
return (
<div>
<div style={{ marginBottom: 20 }}>
<label>
Columns:
<select
value={columnCount}
onChange={(e) => setColumnCount(Number(e.target.value))}
>
<option value={2}>2</option>
<option value={3}>3</option>
<option value={4}>4</option>
<option value={5}>5</option>
</select>
</label>
</div>
<div style={{ height: 600, width: 800 }}>
<Masonry
cellCount={items.length}
cellMeasurerCache={cache}
cellPositioner={cellPositioner}
cellRenderer={cellRenderer}
height={600}
width={800}
/>
</div>
</div>
);
}Grid component with fixed columns and/or rows, creating frozen panes similar to spreadsheet applications.
/**
* Grid with fixed columns and/or rows
* @param props - MultiGrid configuration
*/
function MultiGrid(props: {
/** Optional CSS class name */
className?: string;
/** Class name for bottom-left grid */
classNameBottomLeftGrid?: string;
/** Class name for bottom-right grid */
classNameBottomRightGrid?: string;
/** Class name for top-left grid */
classNameTopLeftGrid?: string;
/** Class name for top-right grid */
classNameTopRightGrid?: string;
/** Total number of columns */
columnCount: number;
/** Fixed width or dynamic width function for columns */
columnWidth: number | ((params: {index: number}) => number);
/** Enable fixed columns on the right instead of left */
enableFixedColumnScroll?: boolean;
/** Enable fixed rows on the bottom instead of top */
enableFixedRowScroll?: boolean;
/** Number of fixed columns */
fixedColumnCount?: number;
/** Number of fixed rows */
fixedRowCount?: number;
/** Height constraint for grid */
height: number;
/** Renderer when no content is present */
noContentRenderer?: () => React.Node;
/** Callback when section is rendered */
onSectionRendered?: (params: {columnStartIndex: number, columnStopIndex: number, rowStartIndex: number, rowStopIndex: number}) => void;
/** Scroll event callback */
onScroll?: (params: {clientHeight: number, clientWidth: number, scrollHeight: number, scrollLeft: number, scrollTop: number, scrollWidth: number}) => void;
/** Number of extra columns to render */
overscanColumnCount?: number;
/** Number of extra rows to render */
overscanRowCount?: number;
/** Total number of rows */
rowCount: number;
/** Fixed height or dynamic height function for rows */
rowHeight: number | ((params: {index: number}) => number);
/** Cell rendering function */
cellRenderer: (params: {columnIndex: number, key: string, rowIndex: number, style: object}) => React.Node;
/** Column index to scroll to */
scrollToColumn?: number;
/** Row index to scroll to */
scrollToRow?: number;
/** Scroll alignment behavior */
scrollToAlignment?: 'auto' | 'end' | 'start' | 'center';
/** Horizontal scroll offset */
scrollLeft?: number;
/** Vertical scroll offset */
scrollTop?: number;
/** Inline styles */
style?: object;
/** Style for bottom-left grid */
styleBottomLeftGrid?: object;
/** Style for bottom-right grid */
styleBottomRightGrid?: object;
/** Style for top-left grid */
styleTopLeftGrid?: object;
/** Style for top-right grid */
styleTopRightGrid?: object;
/** Width constraint for grid */
width: number;
}): React.Component;Usage Examples:
import React from 'react';
import { MultiGrid, AutoSizer } from 'react-virtualized';
// Basic MultiGrid with frozen headers
function SpreadsheetGrid({ data, columnHeaders, rowHeaders }) {
const cellRenderer = ({ columnIndex, key, rowIndex, style }) => {
let content;
let className = 'cell';
if (columnIndex === 0 && rowIndex === 0) {
// Top-left corner
content = '';
className = 'cell header-cell corner-cell';
} else if (rowIndex === 0) {
// Column headers
content = columnHeaders[columnIndex - 1];
className = 'cell header-cell column-header';
} else if (columnIndex === 0) {
// Row headers
content = rowHeaders[rowIndex - 1];
className = 'cell header-cell row-header';
} else {
// Data cells
content = data[rowIndex - 1][columnIndex - 1];
className = 'cell data-cell';
}
return (
<div key={key} className={className} style={style}>
{content}
</div>
);
};
return (
<div style={{ height: 500, width: '100%' }}>
<AutoSizer>
{({ height, width }) => (
<MultiGrid
cellRenderer={cellRenderer}
columnCount={columnHeaders.length + 1}
columnWidth={({ index }) => index === 0 ? 80 : 120}
fixedColumnCount={1}
fixedRowCount={1}
height={height}
rowCount={data.length + 1}
rowHeight={40}
width={width}
classNameTopLeftGrid="grid-top-left"
classNameTopRightGrid="grid-top-right"
classNameBottomLeftGrid="grid-bottom-left"
classNameBottomRightGrid="grid-bottom-right"
/>
)}
</AutoSizer>
</div>
);
}
// Advanced MultiGrid with custom styling
function AdvancedMultiGrid({ salesData }) {
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'];
const salespeople = ['Alice', 'Bob', 'Charlie', 'Diana'];
const getColumnWidth = ({ index }) => {
if (index === 0) return 100; // Names column
return 80; // Data columns
};
const getRowHeight = ({ index }) => {
if (index === 0) return 50; // Header row
return 35; // Data rows
};
const cellRenderer = ({ columnIndex, key, rowIndex, style }) => {
let content = '';
let className = 'multigrid-cell';
if (columnIndex === 0 && rowIndex === 0) {
content = 'Salesperson';
className += ' corner-header';
} else if (rowIndex === 0) {
content = months[columnIndex - 1];
className += ' column-header';
} else if (columnIndex === 0) {
content = salespeople[rowIndex - 1];
className += ' row-header';
} else {
const value = salesData[rowIndex - 1][columnIndex - 1];
content = `$${value.toLocaleString()}`;
className += ' data-cell';
// Add performance-based styling
if (value > 50000) className += ' high-performance';
else if (value > 30000) className += ' medium-performance';
else className += ' low-performance';
}
return (
<div key={key} className={className} style={style}>
{content}
</div>
);
};
return (
<div className="sales-dashboard">
<h2>Sales Performance Dashboard</h2>
<div style={{ height: 400, width: 600 }}>
<MultiGrid
cellRenderer={cellRenderer}
columnCount={months.length + 1}
columnWidth={getColumnWidth}
fixedColumnCount={1}
fixedRowCount={1}
height={400}
rowCount={salespeople.length + 1}
rowHeight={getRowHeight}
width={600}
styleBottomLeftGrid={{ borderRight: '2px solid #ddd' }}
styleTopLeftGrid={{ borderRight: '2px solid #ddd', borderBottom: '2px solid #ddd' }}
styleTopRightGrid={{ borderBottom: '2px solid #ddd' }}
/>
</div>
</div>
);
}Install with Tessl CLI
npx tessl i tessl/npm-react-virtualized