# Building Performant Tables with 100,000 Rows in React

> Large tables look simple until they contain thousands of records.
> 
> Suddenly scrolling becomes laggy, filters feel slow, the browser freezes, and users start wondering if the application has crashed.
> 
> During my experience building enterprise applications, I learned that displaying large datasets is never just about rendering rows. It is about rendering only what the user actually needs.
> 
> Let us see how modern applications handle tables with 100,000 rows without bringing the browser to its knees.

* * *

# The Problem

Imagine an API returns 100,000 employees.

Your first instinct might be to render everything.

```plaintext
<tbody>
  {employees.map(employee => (
    <EmployeeRow
      key={employee.id}
      employee={employee}
    />
  ))}
</tbody>
```

Technically, this works.

Until it does not.

Rendering thousands of DOM elements at once creates several problems.

*   Initial loading becomes slow.
    
*   Scrolling feels choppy.
    
*   Memory usage increases.
    
*   Searching and sorting become expensive.
    
*   The browser spends more time painting than responding.  
    

The problem is not React.

The problem is asking the browser to create far more elements than the user can actually see.

* * *

# The First Rule

**Never render data that is not visible.**

Think about your screen.

Maybe it can display 20 rows.

Why create 100,000 DOM elements when only 20 are visible?

This is where virtualization comes in.

* * *

# Virtualization

Virtualization renders only the rows currently visible on the screen.

As the user scrolls, old rows disappear and new rows are created.

Imagine looking through a window on a moving train.

The landscape changes.

The window stays the same.

Virtualized tables work exactly like that.

```plaintext
100,000 Records

↓

Visible Window

↓

Only 20 to 30 Rows Rendered

↓

Smooth Scrolling
```

Libraries such as **TanStack Virtual** and **react-window** make this surprisingly easy.

* * *

# Server Side Pagination

Fetching every record is usually unnecessary.

Instead of requesting all employees,

```plaintext
GET /employees
```

request only what is needed.

```plaintext
GET /employees?page=1&limit=50
```

When the user moves to the next page, request the next batch.

This reduces network traffic, memory usage, and rendering time.

Enterprise applications almost always follow this approach.

* * *

# Let the Server Do the Heavy Work

Many developers download thousands of records and then filter them inside React.

That works for small datasets.

It does not scale.

Instead of

```plaintext
Download

↓

Filter

↓

Sort

↓

Search
```

do this.

```plaintext
User Searches

↓

API Request

↓

Database Filters

↓

Return Only Matching Rows
```

Databases are designed for searching millions of records.

Browsers are not.

* * *

# Memoization Prevents Unnecessary Work

Imagine a user updates one row.

Should every row render again?

Definitely not.

Components like `React.memo()` help React skip rendering rows that have not changed.

The result is a much smoother experience, especially in large tables.

* * *

# Lazy Loading Improves Perceived Performance

Users care more about how fast an application feels than how fast it actually is.

Instead of making users wait for everything, show the first batch immediately and load additional data as needed.

A responsive interface always feels faster than one showing a loading spinner for several seconds.

* * *

# Small Optimizations Matter

Large tables benefit from many small improvements working together.

Some of my favorites are:

*   Debounce search input before calling the API.
    
*   Keep row components lightweight.
    
*   Avoid unnecessary state updates.
    
*   Use stable keys.
    
*   Memoize expensive calculations.
    
*   Load only required columns.
    
*   Avoid deeply nested components inside every row.  
    

Individually these changes look small.

Together they make a significant difference.

* * *

# The Biggest Lesson

Many developers think performance is about writing faster React code.

In reality, performance is mostly about doing **less work**.

Do not render what users cannot see.

Do not fetch data users have not requested.

Do not calculate values that have not changed.

The fastest code is often the code that never runs.

* * *

# Final Thoughts

Building high performance tables is not about finding a single magic library.

It is about combining smart techniques such as virtualization, server side pagination, efficient searching, memoization, and lazy loading to reduce unnecessary work.

Once you stop thinking about rendering **every row** and start thinking about rendering **only the right rows**, handling 100,000 records becomes much less intimidating.

The next time someone says, "React is slow with large tables," you will know that the real challenge is not React. It is deciding how much work the browser should perform in the first place.
