DEV Community

Cover image for Harnessing the Power of @tanstack/react-virtual with React Hooks: A Step-by-Step Guide
Sheldon Welinga
Sheldon Welinga

Posted on

Harnessing the Power of @tanstack/react-virtual with React Hooks: A Step-by-Step Guide

Introduction: 

In the realm of web development, optimizing performance and efficiency is key to delivering exceptional user experiences. When dealing with large lists or tables, rendering every item in the DOM can lead to performance bottlenecks and increased memory usage. Thankfully, @tanstack/react-virtual comes to the rescue. You can check out the full docs here . In this article, we'll explore what @tanstack/react-virtual is, and guide you through using it step by step, empowering you to customize your virtualized components using React Hooks.

What is @tanstack/react-virtual?

@tanstack/react-virtual is a powerful library that enables efficient rendering of large datasets in React applications. By using virtualization techniques, it only renders the items currently visible on the screen, resulting in improved performance, reduced memory consumption, and a smoother user experience.

Step-by-Step Guide

Step 1: Installation

Begin by installing @tanstack/react-virtual as a dependency in your React project. You can use any package manager of your choice. However in this article we will go with yarn

yarn add @tanstack/react-virtual
Enter fullscreen mode Exit fullscreen mode

Step 2: Importing the Library and React Hooks

After installing the library, import the necessary components and React Hooks into your project. In this example, we'll use the useVirtualizer Hook provided by @tanstack/react-virtual. Import it alongside the required React modules as shown below:

import React, { useRef, useEffect } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';
Enter fullscreen mode Exit fullscreen mode

Step 3: Setting up the Virtualized List

Create a functional component and define the necessary state variables.

function MyVirtualizedList() {
  const parentRef = React.useRef<HTMLDivElement>(null)
  const [listItems, setListItems] = useState<ListItem[]>([])
}
Enter fullscreen mode Exit fullscreen mode
  • The parentRef is used to reference the scrollable element for the list.
  • The listItems is an array of your list which you need to render.

Next, we will fetch out data from an api for the list items and update our state. This article assumes the fetching, so we will use the function getListItems() for that. Our assumption is that the method returns an array of strings.

Please note the array can be of any type depending on what you need to render.

 useEffect(()=>{
    const data = getListItems();
    setListItems(data)
  }, [])
Enter fullscreen mode Exit fullscreen mode

Next, we will create our virtualization using the hook we had imported earlier as below

  const virtualizer = useVirtualizer({
    count: listItems.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 35,
  });
Enter fullscreen mode Exit fullscreen mode
  • The count property in useVirtualizer specifies the total number of items in the list.
  • The getScrollElement property is a callback function that returns the reference to the scrollable element. In this example, it is set to () => parentRef.current to get the scrollable element from the parentRef useRef hook.
  • The estimateSize property provides an estimate of the row size in pixels. In this example, it is set to () => 35.

Next, we will create our actual component to render the context of the virtualized items

function MyVirtualizedList() {
// Previous code goes here

  const virtualItems = virtualizer.getVirtualItems();
  const totalSize = virtualizer.getTotalSize();

  return (
    <>
      <div ref={parentRef} className="overflow-auto h-400">
        <div className="relative w-full" style={{ height: `${totalSize}px` }}>
          {virtualItems.map(virtualItem => {
            const listItem = listItems[virtualItem.index];

            return (
              <div
                key={virtualItem.key}
                className="absolute top-0 left-0 w-full"
                style={{
                  height: `${virtualItem.size}px`,
                  transform: `translateY(${virtualItem.start}px)`,
                }}
              >
                Item: {listItem}
              </div>
            );
          })}
        </div>
      </div>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • getVirtualItems() method returns an array of virtual items currently visible in the viewport. You can iterate over this array to access and modify individual virtual items.
  • getTotalSize() method returns the total size in pixels for the virtualized items. This measurement will incrementally change if you choose to dynamically measure your elements as they are rendered.

While rendering we are mapping th virtualItems, then for each virtual items, we select an item from our listItems depending on the array of that item. You can then render the item as to your preference and depending on the item structure. In our case the assumption was our list is an array of strings.

Step 4: Customizing the List with React Hooks

One of the advantages of using React Hooks is the flexibility to customize your virtualized list. You can modify the state variables, such as count, or update the rendering logic based on your requirements. Additionally, you can incorporate other React Hooks, such as useState or useEffect, to manage additional state or handle side effects.

Step 5: Styling and UI Customization

To style your virtualized list, you can use any css library or write your own plain css. In this article however, we chose to use Tailwind CSS. Therefore you can ensure that Tailwind CSS is installed and configured in your project. Then, modify the JSX code to utilize Tailwind CSS classes to your taste:

Here is the full example

import React, { useRef, useEffect } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';

function MyVirtualizedList() {
  const parentRef = React.useRef<HTMLDivElement>(null)
  const [listItems, setListItems] = useState<ListItem[]>([])

 useEffect(()=>{
    const data = getListItems();
    setListItems(data)
  }, [])

  const virtualizer = useVirtualizer({
    count: listItems.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 35,
  });

  const virtualItems = virtualizer.getVirtualItems();
  const totalSize = virtualizer.getTotalSize();

  return (
    <>
      <div ref={parentRef} className="overflow-auto h-400">
        <div className="relative w-full" style={{ height: `${totalSize}px` }}>
          {virtualItems.map(virtualItem => {
            const listItem = listItems[virtualItem.index]; 

            return (
              <div
                key={virtualItem.key}
                className="absolute top-0 left-0 w-full"
                style={{
                  height: `${virtualItem.size}px`,
                  transform: `translateY(${virtualItem.start}px)`,
                }}
              >
                Item: {listItem}
              </div>
            );
          })}
        </div>
      </div>
    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)