Virtua is a zero-config, fast, and lightweight virtual list and grid component for React that provides efficient rendering of large datasets. It automatically handles virtualization with minimal configuration, making it easy to build performant lists and grids. This guide walks through creating efficient virtualized lists using Virtua with React, covering setup, configuration, and practical implementation patterns. This is part 26 of a series on using Virtua with React.
Prerequisites
Before you begin, ensure you have:
- Node.js version 16.0 or higher
- npm, yarn, or pnpm package manager
- A React project (version 16.8 or higher) with hooks support
- Basic understanding of React hooks (useState, useMemo)
- Familiarity with JavaScript/TypeScript
Installation
Install Virtua using your preferred package manager:
npm install virtua
Or with yarn:
yarn add virtua
Or with pnpm:
pnpm add virtua
Your package.json should include:
{
"dependencies": {
"virtua": "^1.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0"
}
}
Project Setup
Virtua requires minimal setup. No additional configuration is needed beyond installation. For older browsers that don't support ResizeObserver, you may need a polyfill.
First Example / Basic Usage
Let's create a simple virtualized list component. Create src/VirtualizedList.jsx:
// src/VirtualizedList.jsx
import React from 'react';
import { VList } from 'virtua';
function VirtualizedList() {
// Generate a large array of items
const items = Array.from({ length: 10000 }, (_, i) => ({
id: i + 1,
name: `Item ${i + 1}`,
description: `This is item number ${i + 1}`
}));
return (
<div style={{ padding: '20px' }}>
<h2>Virtualized List (10,000 items)</h2>
<VList style={{ height: 600 }}>
{items.map((item) => (
<div
key={item.id}
style={{
padding: '16px',
borderBottom: '1px solid #eee',
height: '60px',
display: 'flex',
alignItems: 'center'
}}
>
<div>
<strong>{item.name}</strong>
<div style={{ fontSize: '14px', color: '#666' }}>
{item.description}
</div>
</div>
</div>
))}
</VList>
</div>
);
}
export default VirtualizedList;
Update your App.jsx:
// src/App.jsx
import React from 'react';
import VirtualizedList from './VirtualizedList';
import './App.css';
function App() {
return (
<div className="App">
<h1>Virtua Example</h1>
<VirtualizedList />
</div>
);
}
export default App;
This creates a virtualized list that efficiently renders 10,000 items by only displaying what's visible.
Understanding the Basics
Virtua uses a simple, declarative API:
- VList: Component for vertical lists
- HList: Component for horizontal lists
- Virtualizer: Lower-level component for custom scroll containers
- WindowVirtualizer: For window-based scrolling
Key concepts:
- Zero Config: Works out of the box with minimal setup
- Automatic Virtualization: Handles virtualization automatically
- Dynamic Heights: Supports variable item heights without configuration
- Render Props: Can use render props for more control
Here's an example with render props:
// src/RenderPropsList.jsx
import React from 'react';
import { VList } from 'virtua';
function RenderPropsList() {
const items = Array.from({ length: 1000 }, (_, i) => ({
id: i + 1,
name: `Item ${i + 1}`,
height: 50 + (i % 5) * 10 // Variable heights
}));
return (
<VList style={{ height: 600 }}>
{(item, index) => (
<div
key={item.id}
style={{
height: item.height,
padding: '16px',
borderBottom: '1px solid #eee',
backgroundColor: index % 2 === 0 ? '#f9f9f9' : 'white'
}}
>
<strong>{item.name}</strong>
</div>
)}
</VList>
);
}
export default RenderPropsList;
Practical Example / Building Something Real
Let's build a comprehensive data table with virtualization:
// src/VirtualizedDataTable.jsx
import React, { useState, useMemo } from 'react';
import { VList } from 'virtua';
interface Employee {
id: number;
name: string;
email: string;
department: string;
salary: number;
status: string;
}
function VirtualizedDataTable() {
const [data] = useState<Employee[]>(() => {
return Array.from({ length: 5000 }, (_, i) => ({
id: i + 1,
name: `Employee ${i + 1}`,
email: `employee${i + 1}@example.com`,
department: ['Engineering', 'Marketing', 'Sales', 'HR'][i % 4],
salary: 50000 + Math.random() * 100000,
status: i % 3 === 0 ? 'Active' : 'Inactive'
}));
});
const renderHeader = () => (
<div
style={{
display: 'grid',
gridTemplateColumns: '80px 200px 250px 150px 120px 120px',
padding: '12px',
backgroundColor: '#f5f5f5',
borderBottom: '2px solid #ddd',
fontWeight: 'bold',
position: 'sticky',
top: 0,
zIndex: 1
}}
>
<div>ID</div>
<div>Name</div>
<div>Department</div>
<div>Salary</div>
<div>Status</div>
<div>Actions</div>
</div>
);
const renderRow = (item: Employee, index: number) => (
<div
key={item.id}
style={{
display: 'grid',
gridTemplateColumns: '80px 200px 250px 150px 120px 120px',
padding: '12px',
borderBottom: '1px solid #eee',
backgroundColor: index % 2 === 0 ? '#f9f9f9' : 'white',
alignItems: 'center',
minHeight: '60px'
}}
>
<div>{item.id}</div>
<div>
<strong>{item.name}</strong>
<div style={{ fontSize: '12px', color: '#666' }}>{item.email}</div>
</div>
<div>
<span style={{
padding: '4px 8px',
borderRadius: '4px',
backgroundColor: '#007bff',
color: 'white',
fontSize: '12px'
}}>
{item.department}
</span>
</div>
<div>${item.salary.toLocaleString()}</div>
<div>
<span style={{
padding: '4px 8px',
borderRadius: '4px',
backgroundColor: item.status === 'Active' ? '#d4edda' : '#f8d7da',
color: item.status === 'Active' ? '#155724' : '#721c24',
fontSize: '12px',
fontWeight: 'bold'
}}>
{item.status}
</span>
</div>
<div>
<button
onClick={() => console.log('Edit', item)}
style={{
padding: '4px 8px',
border: '1px solid #007bff',
backgroundColor: 'white',
color: '#007bff',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Edit
</button>
</div>
</div>
);
return (
<div style={{ padding: '20px' }}>
<h2>Virtualized Data Table (5,000 items)</h2>
<div style={{ border: '1px solid #ddd', borderRadius: '4px' }}>
{renderHeader()}
<VList style={{ height: 600 }}>
{data.map((item, index) => renderRow(item, index))}
</VList>
</div>
</div>
);
}
export default VirtualizedDataTable;
Here's an example using Virtualizer for custom scroll containers:
// src/CustomScrollList.jsx
import React from 'react';
import { Virtualizer } from 'virtua';
function CustomScrollList() {
const items = Array.from({ length: 10000 }, (_, i) => ({
id: i + 1,
name: `Item ${i + 1}`,
height: 50 + (i % 3) * 20
}));
return (
<div
style={{
height: '600px',
overflowY: 'auto',
overflowAnchor: 'none',
border: '1px solid #ddd',
borderRadius: '4px'
}}
>
<div style={{ padding: '20px', backgroundColor: '#f5f5f5' }}>
<h3>Custom Scroll Container</h3>
</div>
<Virtualizer startMargin={80}>
{items.map((item) => (
<div
key={item.id}
style={{
height: item.height,
padding: '16px',
borderBottom: '1px solid #eee'
}}
>
{item.name}
</div>
))}
</Virtualizer>
</div>
);
}
export default CustomScrollList;
This example demonstrates:
- Virtualized list rendering for large datasets
- Grid layout for table-like appearance
- Custom scroll containers with Virtualizer
- Sticky headers
- Variable item heights
- Performance optimization for thousands of items
Common Issues / Troubleshooting
List not rendering: Ensure the container has a fixed
heightstyle. VList requires an explicit height to calculate the viewport.Items not displaying: Verify that you're providing items correctly. For render props, ensure the function signature matches
(item, index) => ReactElement.Scroll issues: When using Virtualizer in a custom scroll container, set
overflowAnchor: 'none'on the container to prevent scroll anchoring issues.Performance issues: Virtua handles performance automatically, but ensure you're not recreating the items array on every render. Use
useMemooruseStatefor stable references.ResizeObserver errors: For older browsers, you may need to install a ResizeObserver polyfill:
npm install resize-observer-polyfill
Next Steps
Now that you understand Virtua:
- Explore HList for horizontal lists
- Learn about WindowVirtualizer for window-based scrolling
- Implement infinite scrolling patterns
- Add item selection and interaction handling
- Explore grid virtualization
- Learn about performance optimization techniques
- Check the official repository: https://github.com/inokawa/virtua
- Look for part 27 of this series for more advanced topics
Summary
You've learned how to set up Virtua and create efficient virtualized lists with minimal configuration. The library provides excellent performance for rendering large datasets with a simple, zero-config API, making it perfect for building data-intensive applications.
SEO Keywords
virtua React
virtua virtualization
virtua tutorial
React virtualized list virtua
virtua installation
React performance optimization
virtua example
React list component
virtua setup
React scroll performance
virtua VList
React virtual list
virtua Virtualizer
React large list rendering
virtua getting started
Top comments (0)