VirtualGrid
The VirtualGrid component efficiently renders large 2D grids by only rendering visible cells. It supports variable row/column sizes, cell spanning (colspan/rowspan), and sticky headers/columns.
When to Use
- VirtualList: For 1D vertical scrolling lists (file trees, chat messages, log viewers)
- VirtualGrid: For 2D grids with both horizontal and vertical scrolling (data tables, spreadsheets, image galleries, dashboards)
Props
| Prop | Type | Default | Description |
|---|---|---|---|
data | T[][] | - | 2D array of cell data |
rowCount | number | - | Number of rows in the grid |
columnCount | number | - | Number of columns in the grid |
rowHeight | number | ((rowIndex: number) => number) | - | Height for each row |
columnWidth | number | ((colIndex: number) => number) | - | Width for each column |
height | number | string | - | Container height |
width | number | string | '100%' | Container width |
overscanRows | number | 2 | Extra rows to render outside visible area |
overscanColumns | number | 2 | Extra columns to render outside visible area |
getCellKey | (row: number, col: number) => string | - | Function to get a unique key for each cell |
getColSpan | (row: number, col: number) => number | - | Function to get column span for a cell |
getRowSpan | (row: number, col: number) => number | - | Function to get row span for a cell |
children | (data: T, row: number, col: number, style: CSSProperties) => ReactNode | - | Render function for each cell |
stickyHeader | boolean | false | Keep first row visible when scrolling |
stickyColumn | boolean | false | Keep first column visible when scrolling |
className | string | - | Additional className for the container |
Basic Usage
A simple grid with fixed cell sizes.
R0C0
R0C1
R0C2
R1C0
R1C1
R1C2
R2C0
R2C1
R2C2
const rowCount = 100;
const columnCount = 20;
const data = Array.from({ length: rowCount }, (_, row) =>
Array.from({ length: columnCount }, (_, col) => ({
row,
col,
value: `R${row}C${col}`
}))
);
<VirtualGrid
data={data}
rowCount={rowCount}
columnCount={columnCount}
rowHeight={36}
columnWidth={100}
height={300}
>
{(cell, row, col, style) => (
<div
style={style}
className="flex items-center justify-center border-r border-b"
>
{cell?.value}
</div>
)}
</VirtualGrid>
Sticky Header
Keep the first row visible while scrolling vertically.
Name
Email
Role
User 1
user1@example.com
Admin
User 2
user2@example.com
User
const columns = ['Name', 'Email', 'Role', 'Status', ...];
<VirtualGrid
data={data}
rowCount={100}
columnCount={columns.length}
rowHeight={36}
columnWidth={120}
height={300}
stickyHeader
>
{(cell, row, col, style) => (
<div
style={style}
className={row === 0 ? 'bg-bg-tertiary font-medium' : 'bg-bg-primary'}
>
{cell}
</div>
)}
</VirtualGrid>
Sticky Column
Keep the first column visible while scrolling horizontally.
Row 1
Row 2
Row 3
792
827
493
928
301
501
<VirtualGrid
data={data}
rowCount={50}
columnCount={15}
rowHeight={36}
columnWidth={(col) => col === 0 ? 80 : 100}
height={300}
stickyColumn
>
{(cell, row, col, style) => (
<div
style={style}
className={col === 0 ? 'bg-bg-tertiary font-medium' : 'bg-bg-primary'}
>
{cell}
</div>
)}
</VirtualGrid>
Sticky Header and Column
Combine sticky header and column for spreadsheet-like behavior.
A
B
1
2
99
42
62
36
const getColumnLetter = (index) => String.fromCharCode(65 + index);
<VirtualGrid
data={data}
rowCount={100}
columnCount={26}
rowHeight={32}
columnWidth={(col) => col === 0 ? 50 : 80}
height={350}
stickyHeader
stickyColumn
>
{(cell, row, col, style) => {
const isHeader = row === 0 || col === 0;
return (
<div
style={style}
className={isHeader ? 'bg-bg-secondary font-medium' : 'bg-bg-primary'}
>
{cell}
</div>
);
}}
</VirtualGrid>
Variable Row Heights
Use a function for rowHeight to specify different heights per row.
R0C0
Expanded row
R0C1
Expanded row
R0C2
Expanded row
R1C0
R1C1
R1C2
R2C0
R2C1
R2C2
<VirtualGrid
data={data}
rowCount={50}
columnCount={5}
rowHeight={(row) => row % 4 === 0 ? 80 : 40}
columnWidth={120}
height={300}
>
{(cell, row, col, style) => (
<div
style={style}
className={cell?.expanded ? 'bg-brand-tertiary' : 'bg-bg-primary'}
>
R{row}C{col}
</div>
)}
</VirtualGrid>
Cell Spanning (Colspan)
Use getColSpan to create cells that span multiple columns.
Date
Event
SF
Event for row 1
LA
Event for row 2
const getColSpan = (row, col) => {
if (col === 1) return 2; // Event column spans 2 cells
return 1;
};
<VirtualGrid
data={data}
rowCount={20}
columnCount={6}
rowHeight={40}
columnWidth={100}
height={300}
getColSpan={getColSpan}
stickyHeader
>
{(cell, row, col, style) => (
<div style={style}>{cell}</div>
)}
</VirtualGrid>
Image Gallery
A practical example showing an image gallery grid.
1
2
3
5
6
7
9
10
11
<VirtualGrid
data={data}
rowCount={20}
columnCount={4}
rowHeight={120}
columnWidth={120}
height={350}
>
{(cell, row, col, style) => (
<div style={style} className="p-1">
<div
className="w-full h-full rounded-uk-md"
style={{ backgroundColor: cell?.color }}
>
{cell?.id + 1}
</div>
</div>
)}
</VirtualGrid>
Types
interface VirtualGridProps<T> {
data: T[][];
rowCount: number;
columnCount: number;
rowHeight: number | ((rowIndex: number) => number);
columnWidth: number | ((colIndex: number) => number);
height: number | string;
width?: number | string;
overscanRows?: number;
overscanColumns?: number;
getCellKey?: (row: number, col: number) => string;
getColSpan?: (row: number, col: number) => number;
getRowSpan?: (row: number, col: number) => number;
children: (data: T, row: number, col: number, style: CSSProperties) => ReactNode;
stickyHeader?: boolean;
stickyColumn?: boolean;
className?: string;
}
Hook: useVirtualGrid
The useVirtualGrid hook can be used to build custom virtualized grid implementations.
import { useVirtualGrid } from '@vuer-ai/vuer-uikit';
const {
visibleRange,
totalWidth,
totalHeight,
getRowOffset,
getColumnOffset,
getRowHeight,
getColumnWidth,
getCellStyle,
} = useVirtualGrid({
rowCount: 100,
columnCount: 20,
rowHeight: 40,
columnWidth: 100,
containerWidth: 800,
containerHeight: 400,
scrollTop: 0,
scrollLeft: 0,
overscanRows: 2,
overscanColumns: 2,
});
Usage
import { VirtualGrid } from '@vuer-ai/vuer-uikit';
function MyComponent() {
const rowCount = 1000;
const columnCount = 50;
const data = Array.from({ length: rowCount }, (_, row) =>
Array.from({ length: columnCount }, (_, col) => `${row}-${col}`)
);
return (
<VirtualGrid
data={data}
rowCount={rowCount}
columnCount={columnCount}
rowHeight={40}
columnWidth={100}
height={400}
stickyHeader
>
{(cell, row, col, style) => (
<div style={style} className="p-2 border">
{cell}
</div>
)}
</VirtualGrid>
);
}