DEV Community

Oliver Reed
Oliver Reed

Posted on

Building High-Performance Virtualized Lists with Virtua in React

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
Enter fullscreen mode Exit fullscreen mode

Or with yarn:

yarn add virtua
Enter fullscreen mode Exit fullscreen mode

Or with pnpm:

pnpm add virtua
Enter fullscreen mode Exit fullscreen mode

Your package.json should include:

{
  "dependencies": {
    "virtua": "^1.0.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  }
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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

  1. List not rendering: Ensure the container has a fixed height style. VList requires an explicit height to calculate the viewport.

  2. Items not displaying: Verify that you're providing items correctly. For render props, ensure the function signature matches (item, index) => ReactElement.

  3. Scroll issues: When using Virtualizer in a custom scroll container, set overflowAnchor: 'none' on the container to prevent scroll anchoring issues.

  4. Performance issues: Virtua handles performance automatically, but ensure you're not recreating the items array on every render. Use useMemo or useState for stable references.

  5. 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)