Skip to content

Chrome renderer freeze with React Suspense and TanStack Table row model #6243

@HaukeSchnau

Description

@HaukeSchnau

Summary

A Vite production build can freeze the Chrome renderer when a trusted pointer click synchronously updates React state, passes a fresh one-row data array into TanStack Table, reads table.getRowModel().rows.length, and mounts a child that suspends on a timer.

The repro is standalone and uses only local state/data. It does not depend on TanStack Router, TanStack Query, React Compiler, Vite React plugin, APIs, auth, or any production app code.

Reproduction

Standalone repro:

https://github.com/HaukeSchnau/trusted-click-table-suspense-freeze-repro

git clone https://github.com/HaukeSchnau/trusted-click-table-suspense-freeze-repro.git
cd trusted-click-table-suspense-freeze-repro
pnpm install
pnpm check
pnpm preview:repro

Then open http://127.0.0.1:4174/ in Chrome and click Synchronous active row.

Expected behavior

The click completes and the page remains responsive. The Suspense boundary should handle the thrown timer promise and resume rendering normally.

Actual behavior

The Chrome tab freezes during the click. Reloading or closing the tab is required to recover.

Environment

  • React 19.2.3
  • @tanstack/react-table 8.21.3
  • Vite 7.3.0
  • Chrome
  • macOS

Reduced trigger

const columns: [] = [];
const coreRowModel = getCoreRowModel();
let resolved = false;
let promise: Promise<void> | undefined;

function Suspender() {
  if (!resolved) {
    promise ??= new Promise((resolve) => {
      window.setTimeout(() => {
        resolved = true;
        resolve();
      });
    });

    throw promise;
  }

  return null;
}

function App() {
  const [active, setActive] = useState(false);
  const table = useReactTable({
    data: active ? [null] : [],
    columns,
    getCoreRowModel: coreRowModel,
  });

  return (
    <>
      {table.getRowModel().rows.length ? (
        <Suspense fallback={null}>
          <Suspender />
        </Suspense>
      ) : null}

      <button id="sync-active" onClick={() => setActive(true)}>
        Synchronous active row
      </button>
    </>
  );
}

Notes

The smallest trigger I’ve found is:

trusted click -> synchronous React state update -> fresh TanStack Table data -> table.getRowModel() read -> React Suspense child suspends on a timer -> Chrome renderer freeze.

This might ultimately be an interaction with React Suspense, but the repro becomes responsive when the TanStack Table row-model read is removed, so I’m filing here first.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions