Skip to content

Instantly share code, notes, and snippets.

@MansourM61
Created January 8, 2026 09:20
Show Gist options
  • Select an option

  • Save MansourM61/02db7b6f746806f3f4ae6e87df39451b to your computer and use it in GitHub Desktop.

Select an option

Save MansourM61/02db7b6f746806f3f4ae6e87df39451b to your computer and use it in GitHub Desktop.

TanStack

TanStack provides a large range of tools that can be used in vanilla JS/TS or React framework.

TanStack Virtual

TanStack Virtual is a headless UI utility for virtualizing long lists of elements.

To install:

npm install @tanstack/react-virtual

Fixed-Size Contents

import { useVirtualizer } from "@tanstack/react-virtual";

function Component() {
  // The scrollable element for your list
  const parentRef = React.useRef(null);

  // The virtualizer
  const rowVirtualizer = useVirtualizer({
    count: 10000, // total number of items
    getScrollElement: () => parentRef.current, // reference to the container of the list
    estimateSize: () => 35, // approximate size of each individual item in the list
    horizontal: false, // decides the direction of the list. For horizontal lists, width and X transformation will be used in the following code
  });

  return (
    <>
      {/* The scrollable element for your list */}
      <div
        ref={parentRef}
        style={{
          height: `400px`,
          overflow: "auto", // Make it scroll!
        }}
      >
        {/* The large inner element to hold all of the items */}
        <div
          style={{
            height: `${rowVirtualizer.getTotalSize()}px`,
            width: "100%",
            position: "relative",
          }}
        >
          {/* Only the visible items in the virtualizer, manually positioned to be in view */}
          {rowVirtualizer.getVirtualItems().map((virtualItem) => (
            <div
              key={virtualItem.key}
              style={{
                position: "absolute",
                top: 0,
                left: 0,
                width: "100%",
                height: `${virtualItem.size}px`,
                transform: `translateY(${virtualItem.start}px)`,
              }}
            >
              Row {virtualItem.index}
            </div>
          ))}
        </div>
      </div>
    </>
  );
}

Variable-Size Contents

import React from "react";
import { useVirtualizer } from "@tanstack/react-virtual";

function Component({ rows }: { rows: Array<number> }) {
  const parentRef = React.useRef<HTMLDivElement>(null);

  const rowVirtualizer = useVirtualizer({
    count: rows.length,
    getScrollElement: () => parentRef.current, // reference to the container
    estimateSize: (i) => rowsHeigh[i], // predefined row heights for the ith element
    overscan: 5, // the number of items to render above and below the visible area
  });

  return (
    <>
      <div
        ref={parentRef}
        className="List"
        style={{
          height: `200px`,
          width: `400px`,
          overflow: "auto",
        }}
      >
        <div
          style={{
            height: `${rowVirtualizer.getTotalSize()}px`,
            width: "100%",
            position: "relative",
          }}
        >
          {rowVirtualizer.getVirtualItems().map((virtualRow) => (
            <div
              key={virtualRow.index}
              style={{
                position: "absolute",
                top: 0,
                left: 0,
                width: "100%",
                height: `${rows[virtualRow.index]}px`,
                transform: `translateY(${virtualRow.start}px)`,
              }}
            >
              Row {virtualRow.index}
            </div>
          ))}
        </div>
      </div>
    </>
  );
}

Dynamic Size Contents

import React from "react";
import { useVirtualizer } from "@tanstack/react-virtual";

function Component({ rows }: { rows: Array<number> }) {
  const parentRef = React.useRef < HTMLDivElement > null;

  const rowVirtualizer = useVirtualizer({
    count: rows.length,
    getScrollElement: () => parentRef.current, // reference to the container
    estimateSize: (i) => rowsHeigh[i], // predefined row heights for ith element
    overscan: 5,
  });

  return (
    <>
      <div
        ref={parentRef}
        className="List"
        style={{
          height: `200px`,
          width: `400px`,
          overflow: "auto",
        }}
      >
        <div
          style={{
            height: `${rowVirtualizer.getTotalSize()}px`,
            width: "100%",
            position: "relative",
          }}
        >
          <div
            style={{
              position: "absolute",
              top: 0,
              left: 0,
              width: "100%",
              transform: `translateY(${rowVirtualizer[0]?.start ?? 0}px)`,
            }}
          >
            {rowVirtualizer.getVirtualItems().map((virtualRow, index) => (
              <div key={virtualRow.index} data-index={index}>
                Row {virtualRow.index}
              </div>
            ))}
          </div>
        </div>
      </div>
    </>
  );
}

Window-Scrolled Contents

import * as React from "react";
import * as ReactDOM from "react-dom/client";

import { useWindowVirtualizer } from "@tanstack/react-virtual";

function Example() {
  const listRef = React.useRef<HTMLDivElement | null>(null);

  const virtualizer = useWindowVirtualizer({
    count: 10000,
    estimateSize: () => 35,
    overscan: 5,
    scrollMargin: listRef.current?.offsetTop ?? 0, // this value represents the space between the beginning of the scrolling element and the start of the list.
  });

  return (
    <>
      <div ref={listRef} className="List">
        <div
          style={{
            height: `${virtualizer.getTotalSize()}px`,
            width: "100%",
            position: "relative",
          }}
        >
          {virtualizer.getVirtualItems().map((item) => (
            <div
              key={item.key}
              style={{
                position: "absolute",
                top: 0,
                left: 0,
                width: "100%",
                height: `${item.size}px`,
                transform: `translateY(${
                  item.start - virtualizer.options.scrollMargin
                }px)`,
              }}
            >
              Row {item.index}
            </div>
          ))}
        </div>
      </div>
    </>
  );
}

TanStack Pacer

TanStack Pacer is a library focused on providing high-quality utilities for controlling function execution timing in your applications.

  1. To install:

    npm install @tanstack/react-pacer
    npm -D install @tanstack/react-devtools @tanstack/react-pacer-devtools
  2. Add DevTools to top level parent:

import { TanStackDevtools } from "@tanstack/react-devtools";
import { pacerDevtoolsPlugin } from "@tanstack/react-pacer-devtools";

function App() {
  return (
    <div>
      {/* Your app content */}

      <TanStackDevtools
        eventBusConfig={{
          debug: false,
        }}
        plugins={[pacerDevtoolsPlugin()]}
      />
    </div>
  );
}

Debouncing

Debouncer executes a function after a period of inactivity while rejecting other calls during activity. It comes in both sync and async versions.

import { useDebouncedCallback } from '@tanstack/pacer'

// Debounce a search handler
const handleSearch = useDebouncedCallback((query: string) => {
        fetchSearchResults(query);  // function to debounce
    }, {
        wait: 500 // Wait 500ms between executions
    });  // creates a debounced version of a callback function

// Use in an input
<input
  type="search"
  onChange={(e) => handleSearch(e.target.value)}
/>

To get more control, use useDebouncer hook:

import { useDebouncer } from '@tanstack/pacer'

// Debounce a search handler
const handleSearch = useDebouncer((query: string) => {
        fetchSearchResults(query);  // function to debounce
    }, {
        wait: 500 // Wait 500ms between executions
    });  // creates a debouncer object

handleSearch.cancel() // Cancel pending execution
handleSearch.flush() // Flush pending execution immediately
handleSearch.setOptions({ wait: 1000 }) // Increase wait time
// Use in an input
<input
  type="search"
  onChange={(e) => handleSearch.maybeExecute(e.target.value)}
/>

Throttle

Throttler executes a function at regular intervals while rejecting all but one call during each interval.

import { useThrottledState } from '@tanstack/pacer'

// throttle a state update
const [
        value, // throttled state value
        setValue, // throttled setter function that respects the configured wait time
        throttler // throttler instance for additional control
    ] = useThrottledState(initialState, 
        {
        wait: 500 // Wait 500ms between state updates
        leading: true,   // Update immediately on first change
        trailing: false  // Skip trailing edge updates
        },
        (state) => ({
            isPending: state.isPending,
        })
);  // creates a throttled state value that updates at most once within a specified time window

// ...

// Access throttler methods if needed
const handleReset = () => {
  setValue(0);
  throttler.cancel(); // Cancel any pending updates
};

// Access the selected throttler state (will be empty object {} unless selector provided)
const { isPending } = throttler.state;

If updating the value is the only required function, useThrottledValue can be used.

Rate Limiting

Rate Limiter prevents a function from being called too frequently while rejects calls when the limit is reached.

// Rate limit API calls to maximum 5 calls per minute with a sliding window
const makeApiCall = useRateLimitedCallback(
  async (data: ApiData) => {
    return await fetch('/api/endpoint', { method: 'POST', body: JSON.stringify(data) });
  },
  {
    limit: 5, // maximum number of executions allowed within the time window.
    window: 60000, // 1 minute time window
    windowType: 'sliding',  // allows executions as old ones expire; `fixed` resets after the window period
    onReject: () => {
      console.warn('API rate limit reached. Please wait before trying again.');
    }
  }
); // creates a rate-limited version of a callback function

Queueing

Queuer processes all calls to a function in order while only rejects calls if the queue is full.

import { queue } from '@tanstack/pacer'

// Create a queue that processes items every second
const processItems = queue<number>(
  (item: number) => {
    // Process each item
    console.log('Processing:', item)
  },
  {
    wait: 1000, // Wait 1 second between processing items
    maxSize: 10, // Optional: limit queue size to prevent memory or time issues. If not specified, queue will not get full
    onItemsChange: (queuer) => {
      console.log('Current queue:', queuer.peekAllItems())
    } // callback fired whenever an item is added or removed from the queuer
  }
)

// Add items to be processed
processItems(1) // Processed immediately
processItems(2) // Processed after 1 second
processItems(3) // Processed after 2 seconds

Batching

Batcher groups multiple function calls into a single batch with No rejections. Unlike Queuing, which ensures every operation is processed individually, batching collects items and processes them in configurable groups, improving efficiency and reducing overhead.

Batching can be triggered by:

  • Reaching a maximum batch size
  • Waiting a maximum amount of time
  • Custom logic (e.g., a special item or condition)
import { batch } from '@tanstack/pacer'

// Create a batcher that processes up to 3 items or every 2 seconds
const processBatch = batch<number>(
  (items) => {
    // Process the batch
    console.log('Processing batch:', items)
  },
  {
    maxSize: 3, // Process when 3 items are collected
    wait: 2000, // Or after 2 seconds, whichever comes first
    onItemsChange: (batcher) => {
      console.log('Current batch:', batcher.peekAllItems())
    }
  }
)

// Add items to be batched
processBatch(1)
processBatch(2)
processBatch(3) // Triggers batch processing
processBatch(4)
// Or wait 2 seconds for the next batch to process
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment