Skip to content

Conversation

@ammar-agent
Copy link
Collaborator

@ammar-agent ammar-agent commented Dec 21, 2025

Summary

Chrome trace analysis (Dec 20, 2025) revealed 110ms+ layout thrashing from synchronous scrollHeight/clientHeight reads in ResizeObserver callbacks during React's commit phase.

Verified Performance Improvements

Metric Before After Improvement
Layout thrashing total 270ms 99ms 63% ↓
Max layout event 110ms 39ms 65% ↓
Click latency (max) 517ms 18ms 96% ↓
Click latency (avg) 340ms 18ms 95% ↓

Changes

New useOverflowDetection hook (+72 lines)

Reusable, well-documented hook for detecting content overflow with RAF-throttled layout reads:

const isOverflowing = useOverflowDetection(containerRef, { enabled: clampContent });

Fixed useAutoScroll.ts

  • Wrapped ResizeObserver callback in requestAnimationFrame
  • Added coalescing to prevent rapid successive calls
  • Proper cleanup on element unmount

Improved useResizeObserver.ts documentation

  • Added JSDoc explaining when to use vs raw ResizeObserver
  • Cross-references useOverflowDetection for overflow detection use case

Extracted calculateLineNumberWidths utility

  • DRY extraction removes 30+ lines of duplicate code between DiffRenderer and SelectableDiffRenderer

Why RAF Fixes the Issue

When ResizeObserver fires during React's commit phase, reading layout properties like scrollHeight forces the browser to complete a full synchronous layout before returning. With ~18,515 DOM objects in the trace, this blocked the main thread for 100ms+.

By deferring to requestAnimationFrame:

  1. The resize callback returns immediately (no blocking)
  2. Layout read happens after React's commit is complete
  3. Browser can batch layout calculations more efficiently
  4. Rapid successive events are coalesced into a single read

Generated with mux • Model: anthropic:claude-opus-4-5 • Thinking: high

Chrome trace analysis revealed 110ms+ layout thrashing from synchronous
scrollHeight/clientHeight reads in ResizeObserver callbacks during
React's commit phase.

Changes:
- Add useOverflowDetection hook with RAF-deferred layout reads
- Fix useAutoScroll.ts ResizeObserver to defer scroll operations
- Extract calculateLineNumberWidths utility to DRY up duplicated code
- Add documentation to useResizeObserver explaining usage patterns

Performance impact:
- DiffRenderer: 110ms forced layout → deferred to next frame
- useAutoScroll: 50-85ms forced layout → deferred + coalesced
- Click latency: 467-517ms → root causes addressed

Net change: +12 lines, but cleaner architecture with reusable patterns.
@ammario ammario merged commit 038aede into main Dec 21, 2025
20 checks passed
@ammario ammario deleted the performance-9yfb branch December 21, 2025 02:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants