npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-table-virtualized

v0.1.1

Published

Seamless virtualization for TanStack Table using TanStack Virtual

Readme

react-table-virtualized

Seamless virtualization for TanStack Table using TanStack Virtual

npm version License: MIT

Features

  • 🎯 Single Hook API - One hook to rule them all
  • 🪶 Lightweight - < 1KB gzipped
  • Zero Dependencies - Only peer dependencies
  • 💪 TypeScript - Full type safety out of the box
  • 🔥 Fully Compatible - Works with all TanStack Table features (sorting, filtering, grouping, etc.)
  • 📦 Tree-shakeable - ESM and CJS builds

Why?

TanStack Table is amazing for managing table state, but it doesn't handle virtualization. TanStack Virtual is perfect for virtualizing lists, but it doesn't know about tables. This package seamlessly wires them together with a simple, intuitive API.

The result? Render 50,000+ rows smoothly while only keeping ~20 DOM nodes in memory.

Installation

npm install react-table-virtualized @tanstack/react-table @tanstack/react-virtual
yarn add react-table-virtualized @tanstack/react-table @tanstack/react-virtual
pnpm add react-table-virtualized @tanstack/react-table @tanstack/react-virtual

Quick Start

import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table'
import { useVirtualTable } from 'react-table-virtualized'

function MyTable({ data, columns }) {
  // 1. Set up react-table as usual
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
  })

  // 2. Add virtualization with one hook
  const { rows, virtualizer, containerRef } = useVirtualTable({
    table,
    estimateSize: 35, // estimated row height in pixels
    overscan: 5, // number of rows to render outside visible area
  })

  // 3. Render
  return (
    <div ref={containerRef} style={{ height: '500px', overflow: 'auto' }}>
      <div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
        {/* Header */}
        <div style={{ display: 'flex', position: 'sticky', top: 0 }}>
          {table.getHeaderGroups().map((headerGroup) =>
            headerGroup.headers.map((header) => (
              <div key={header.id} style={{ width: header.getSize() }}>
                {flexRender(header.column.columnDef.header, header.getContext())}
              </div>
            ))
          )}
        </div>

        {/* Virtual Rows */}
        {rows.map((virtualRow) => {
          const row = table.getRowModel().rows[virtualRow.index]
          return (
            <div
              key={row.id}
              style={{
                position: 'absolute',
                top: 0,
                left: 0,
                width: '100%',
                height: `${virtualRow.size}px`,
                transform: `translateY(${virtualRow.start}px)`,
              }}
            >
              {row.getVisibleCells().map((cell) => (
                <div key={cell.id} style={{ width: cell.column.getSize() }}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </div>
              ))}
            </div>
          )
        })}
      </div>
    </div>
  )
}

That's it! You now have a fully virtualized table that works with all TanStack Table features.

API Reference

useVirtualTable(options)

The main hook that connects TanStack Table with TanStack Virtual.

Parameters

interface UseVirtualTableOptions<TData> {
  /** The TanStack Table instance */
  table: Table<TData>

  /** Estimated row size in pixels, or function to calculate per row */
  estimateSize: number | ((index: number) => number)

  /** Number of items to render outside visible area (default: 5) */
  overscan?: number

  /** Custom scroll element getter */
  getScrollElement?: () => HTMLElement | null

  /** Custom element measurement function for dynamic heights */
  measureElement?: (element: Element, entry: ResizeObserverEntry | undefined, instance: Virtualizer) => number

  /** Custom range extractor */
  rangeExtractor?: (range: Range) => number[]
}

Returns

interface UseVirtualTableReturn {
  /** Virtual row items to render */
  rows: VirtualItem[]

  /** The virtualizer instance from @tanstack/react-virtual */
  virtualizer: Virtualizer<HTMLElement, Element>

  /** Ref to attach to the scrollable container */
  containerRef: RefObject<HTMLDivElement | null>
}

Options Details

  • table (required): Your TanStack Table instance from useReactTable()

  • estimateSize (required): Either a fixed number or a function that returns the estimated height for each row. Used for scroll calculation.

    // Fixed height
    estimateSize: 35
    
    // Dynamic height
    estimateSize: (index) => {
      const row = table.getRowModel().rows[index]
      return row.original.description.length > 100 ? 80 : 40
    }
  • overscan (optional, default: 5): Number of rows to render outside the visible area. Higher values improve scroll smoothness but use more memory.

  • getScrollElement (optional): Custom function to get the scroll container. By default, uses containerRef.

  • measureElement (optional): Custom measurement function for dynamic row heights. Used with virtualizer.measureElement ref callback.

  • rangeExtractor (optional): Custom range extraction logic from TanStack Virtual.

Examples

Basic Example (Fixed Heights)

Simple table with 10,000 rows, sorting, and filtering:

const { rows, virtualizer, containerRef } = useVirtualTable({
  table,
  estimateSize: 35, // All rows are 35px tall
  overscan: 5,
})

See full example →

Dynamic Heights Example

Rows with variable content and expandable sub-rows:

const { rows, virtualizer, containerRef } = useVirtualTable({
  table,
  estimateSize: (index) => {
    const row = table.getRowModel().rows[index]
    return estimateRowHeight(row) // Your custom logic
  },
  measureElement: virtualizer.measureElement, // Enable dynamic measurement
})

See full example →

Real-World Example

50,000 rows with all features (search, filter, sort, selection):

const { rows, virtualizer, containerRef } = useVirtualTable({
  table,
  estimateSize: 40,
  overscan: 10, // Render more for smoother scrolling
})

See full example →

TypeScript

This package is written in TypeScript and provides full type safety:

import { useVirtualTable, UseVirtualTableOptions, UseVirtualTableReturn } from 'react-table-virtualized'
import type { Table } from '@tanstack/react-table'

// Type inference works automatically
const { rows, virtualizer, containerRef } = useVirtualTable({
  table, // Table<MyDataType>
  estimateSize: 35,
})

// Or explicitly type the options
const options: UseVirtualTableOptions<MyDataType> = {
  table,
  estimateSize: 35,
}

const result: UseVirtualTableReturn = useVirtualTable(options)

Performance Tips

  1. Use estimateSize wisely: More accurate estimates = smoother scrolling
  2. Adjust overscan: Higher values = smoother scroll but more DOM nodes
  3. Memoize your columns: Prevents unnecessary re-renders
  4. Use useMemo for data: Especially with sorting/filtering
  5. Enable dynamic measurement only when needed: It has a small performance cost

FAQ

Does this work with sorting and filtering?

Yes! It works seamlessly with all TanStack Table features:

  • ✅ Sorting
  • ✅ Filtering
  • ✅ Grouping
  • ✅ Pagination
  • ✅ Column resizing
  • ✅ Row selection
  • ✅ Expanding rows
  • ✅ Everything else

The virtualization layer sits on top of the table state, so any changes to the table automatically reflect in the virtualized view.

Can I use dynamic row heights?

Yes! Use the measureElement option:

const { rows, virtualizer, containerRef } = useVirtualTable({
  table,
  estimateSize: (index) => estimateHeight(index),
  measureElement: virtualizer.measureElement,
})

// In your render:
<div
  ref={virtualizer.measureElement}
  data-index={virtualRow.index}
  style={{ position: 'absolute', transform: `translateY(${virtualRow.start}px)` }}
>
  {/* Your content */}
</div>

What about column virtualization?

Column virtualization is not currently supported. In practice, row virtualization solves 99% of performance problems. Most tables have < 20 columns, which is perfectly fine for the DOM.

If you have 100+ columns and need column virtualization, please open an issue!

How do I add sticky headers?

Use CSS! The package doesn't handle sticky headers because it's purely a CSS concern:

<div style={{ position: 'sticky', top: 0, zIndex: 1, backgroundColor: 'white' }}>
  {/* Header content */}
</div>

Can I scroll to a specific row?

Yes! Use the virtualizer instance:

const { virtualizer } = useVirtualTable({ table, estimateSize: 35 })

// Scroll to row index 100
virtualizer.scrollToIndex(100, { align: 'start' })

Does this work with React Server Components?

The core hook uses React hooks (useRef, useMemo) so it needs to run on the client. Mark your component with 'use client':

'use client'

import { useVirtualTable } from 'react-table-virtualized'

How much memory does this save?

A lot! Without virtualization, a 50,000 row table creates 50,000 DOM nodes. With virtualization, you only render what's visible (~20 rows).

Memory usage:

  • Without: ~50,000 DOM nodes
  • With: ~20-30 DOM nodes (depending on viewport and overscan)

That's 99.9% fewer DOM nodes!

Browser Support

  • Chrome/Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • React 16.8+ (hooks support)

Contributing

Contributions are welcome! Please open an issue or PR.

License

MIT © Yarin Keren

Credits

Built on top of:

Related