Note • Feb 9, 2026

Building a Data-Rich Table for 100k Transactions

Date
Feb 9, 2026
Tags
performancepagination
Demo
Live demo

Orders

100,000 orders
Applying filters...
Loading...
ID ↓Order # Date/Time Customer Method Dept/CC Type Total Amount Status
Loading orders...
Page of 1
Showing 1 - 100 of 100,000

Paginated table for 100k rows.

The Problem

I started with a few hundred transactions. Then thousands. Before I knew it, 100,000.

Loading everything at once froze the browser. I needed a different approach.

What I Built

A table that handles 100,000 transactions smoothly. The goal? Show I can display huge amounts of data without performance issues.

Here’s what it does:

  • Shows 100 rows at a time with pagination
  • Click any row to expand details
  • Search waits until you stop typing, caches pages for instant navigation

I’m using mock data here because it’s the simplest way to demonstrate this. In production, I’d connect to a real API. The same optimizations apply.

How It Works

Data generation:

  • Transactions created on demand from index numbers. Fast and consistent, with individual transactions cached.
// No 100k-row array in memory — each row is derived from its index on demand.
function generateTransaction(index) {
  if (cache.has(index)) return cache.get(index);   // small bounded cache

  const seed = index * 9301 + 49297;               // deterministic, repeatable
  return {
    id: index + 1,
    transactionId: `TXN-${String(index + 1).padStart(8, '0')}`,
    merchant: merchants[seed % merchants.length],
    status:   statuses[weightedStatus(seed)],
    // …rest of the fields derived from `seed`
  };
}

Page caching:

  • Entire page results cached based on page, filters, search, and sort. Navigating back returns instantly. Cache stores up to 50 page queries to balance performance with memory.
// Cache whole page results; drop the oldest when full (simple LRU).
if (pageCache.size >= PAGE_CACHE_SIZE) {
  pageCache.delete(pageCache.keys().next().value);
}
pageCache.set(cacheKey, result);

Search:

  • Waits 300ms after you stop typing. Filtering happens in chunks to keep the UI responsive. Only the current page renders.

Expandable rows:

  • Click any row to see details. Keeps the interface clean.

Performance

With my optimizations:

  • Loads in under 100ms, navigation feels instant. Uses 2-5MB of memory for 100k transactions.

Without them:

  • Browser freezes, uses hundreds of MB of memory, takes 5-10 seconds to load.

The difference is huge. These techniques work for any app handling large datasets.

What I Learned

  • Always test with real data sizes. 100 transactions doesn’t tell you if 100,000 will work. Pagination is essential for large datasets.
  • Debouncing search prevents unnecessary work. Caching transactions and pages makes everything faster.
  • Expandable rows keep interfaces clean without overwhelming users.

These patterns work everywhere. Pagination, memoization, debouncing, page caching. They all apply in production apps with real APIs too.

That’s It

100,000 transactions. 100 rendered at a time. Fast, clean, and doesn’t overwhelm users.