From 1abb0dc1037e948438de9d3b9be529dc4bb9d7e3 Mon Sep 17 00:00:00 2001 From: Hernan Javier Ardila Sanchez Date: Wed, 24 Jun 2026 16:28:46 +0200 Subject: [PATCH] fix(chat): set scroll position before paint on session-switch replay (#1730) Replace useEffect with useLayoutEffect in the pendingInitialRestoreRef replay so restoreSnapshot runs synchronously after DOM commit, before the browser paints. Prevents visible flash of content at the wrong scroll position when the scroll container mounts after session hydration. Adapted from openchamber/openchamber#1553 (Fix 2). The virtualVersion counter (Fix 1) is not applicable: virtua (post #1651) does not use useVirtualizer's useState-based instance pattern that motivated it. Validation: - bun --cwd packages/ui type-check - no new errors in useChatAutoFollow.ts - bun --cwd packages/ui lint - passed Co-authored-by: herjarsa --- packages/ui/src/hooks/useChatAutoFollow.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/hooks/useChatAutoFollow.ts b/packages/ui/src/hooks/useChatAutoFollow.ts index 59c281bd..7b37e272 100644 --- a/packages/ui/src/hooks/useChatAutoFollow.ts +++ b/packages/ui/src/hooks/useChatAutoFollow.ts @@ -411,7 +411,9 @@ export const useChatAutoFollow = ({ }, [sessionIsWorking, startFollowLoop]); // Replay a deferred restoreSnapshot once ChatViewport mounts. - React.useEffect(() => { + // useLayoutEffect ensures scroll position is set before the browser paints, + // preventing a visible flash of content at the wrong scroll position. + React.useLayoutEffect(() => { if (!containerEl) return; if (pendingInitialRestoreRef.current && pendingInitialRestoreRef.current === currentSessionId) { void restoreSnapshot();