Skip to content

Switch message timeline virtualization to Virtuoso#1933

Open
juliusmarminge wants to merge 2 commits intomainfrom
t3code/move-timeline-to-virtuoso
Open

Switch message timeline virtualization to Virtuoso#1933
juliusmarminge wants to merge 2 commits intomainfrom
t3code/move-timeline-to-virtuoso

Conversation

@juliusmarminge
Copy link
Copy Markdown
Member

@juliusmarminge juliusmarminge commented Apr 11, 2026

Summary

  • Replaced the custom @tanstack/react-virtual timeline implementation with react-virtuoso in MessagesTimeline.
  • Added row height measurement and virtualization plumbing to keep offscreen timeline rows stable as content wraps and images load.
  • Updated browser virtualization scenarios and assertions to validate the new rendered virtual row behavior.
  • Added react-virtuoso to the web package dependencies.

Testing

  • Not run (PR content drafted from the provided diff).
  • bun fmt
  • bun lint
  • bun typecheck
  • bun run test

Note

Medium Risk
Medium risk because it replaces the core chat timeline virtualization/auto-scroll implementation; regressions could affect scroll position, rendering performance, or row height stability (especially with images and dynamic content).

Overview
Switches chat message timeline virtualization from @tanstack/react-virtual to react-virtuoso. MessagesTimeline now renders rows via Virtuoso with per-row height estimates, a padded virtualization viewport, and new List/Item wrappers that expose data-virtual-row-* attributes.

Auto-scroll behavior is simplified: instead of custom size-change scroll adjustment and snapshot reporting, the timeline uses followOutput and triggers autoscrollToBottom() on image load only when the scroll container is near the bottom.

Browser virtualization tests are updated to validate sizing and stability by reading the new virtual-row DOM attributes (and adjusting tolerances), and react-virtuoso is added to web dependencies.

Reviewed by Cursor Bugbot for commit 366651f. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Replace TanStack Virtual with Virtuoso for message timeline virtualization

  • Replaces useVirtualizer from TanStack React Virtual with react-virtuoso in MessagesTimeline, enabling full list virtualization for all rows.
  • Adds followOutput behavior so the timeline auto-scrolls to the bottom when the user is already near the bottom, using a VirtuosoHandle ref and isScrollContainerNearBottom.
  • Removes the "tail" non-virtualized rendering logic, firstUnvirtualizedRowIndex, onVirtualizerSnapshot, and all snapshot/measurement wiring tied to the old virtualizer.
  • Updates the browser test harness to read virtualized row sizes via data-virtual-row-size and assert size stability instead of transition semantics.
  • Behavioral Change: onVirtualizerSnapshot is no longer accepted or invoked; tail rows are no longer force-rendered outside virtualization.

Macroscope summarized 366651f.

- Replace TanStack virtualizer with react-virtuoso
- Update timeline height measurement and virtualization tests
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 99b840f9-b109-4a47-8c11-394c141c0e22

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/move-timeline-to-virtuoso

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. labels Apr 11, 2026
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Stale height cache used on width-triggered Virtuoso remount
    • Replaced the useEffect-based cache clear with React's synchronous 'update state during rendering' pattern (tracking prevVirtualizationWidthKey in state) so measuredRowHeightsById is cleared before the virtualizedRowHeightEstimates memo runs on the same render.

Create PR

Or push these changes by commenting:

@cursor push 42d29fad02
Preview (42d29fad02)
diff --git a/apps/web/src/components/chat/MessagesTimeline.tsx b/apps/web/src/components/chat/MessagesTimeline.tsx
--- a/apps/web/src/components/chat/MessagesTimeline.tsx
+++ b/apps/web/src/components/chat/MessagesTimeline.tsx
@@ -278,6 +278,16 @@
     () => (canVirtualize ? rows.slice(0, virtualizedRowCount) : []),
     [canVirtualize, rows, virtualizedRowCount],
   );
+
+  const virtualizationWidthKey =
+    timelineWidthPx === null ? "width:unknown" : `width:${Math.round(timelineWidthPx)}`;
+  const [prevVirtualizationWidthKey, setPrevVirtualizationWidthKey] =
+    useState(virtualizationWidthKey);
+  if (virtualizationWidthKey !== prevVirtualizationWidthKey) {
+    setPrevVirtualizationWidthKey(virtualizationWidthKey);
+    setMeasuredRowHeightsById({});
+  }
+
   const virtualizedRowHeightEstimates = useMemo(
     () =>
       virtualizedRows.map((row) => {
@@ -300,12 +310,7 @@
     ],
   );
   const nonVirtualizedRows = canVirtualize ? rows.slice(virtualizedRowCount) : rows;
-  const virtualizationWidthKey =
-    timelineWidthPx === null ? "width:unknown" : `width:${Math.round(timelineWidthPx)}`;
   const defaultVirtualizedRowHeight = virtualizedRowHeightEstimates[0];
-  useEffect(() => {
-    setMeasuredRowHeightsById({});
-  }, [virtualizationWidthKey]);
 
   const renderRowContent = (row: TimelineRow) => (
     <div

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 56dd6e1. Configure here.

@macroscopeapp
Copy link
Copy Markdown
Contributor

macroscopeapp bot commented Apr 11, 2026

Approvability

Verdict: Needs human review

This PR replaces the virtualization library for the chat message timeline, swapping @tanstack/react-virtual for react-virtuoso. While the author wrote the original code and the change simplifies the implementation, this affects runtime scroll and rendering behavior in a core UI component, warranting human verification.

You can customize Macroscope's approvability policy. Learn more.

- Remove manual tail rendering and row height tracking
- Use Virtuoso for the full timeline and keep autoscroll stable
- Update virtualization harness expectations for appended rows
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant