Skip to content

Conversation

@mattcosta7
Copy link
Contributor

@mattcosta7 mattcosta7 commented Dec 15, 2025

Summary

Performance optimizations for useResizeObserver and useOverflow hooks to improve INP, with opt-in throttling for backwards compatibility.

Changes

  1. useResizeObserver - Added optional throttle parameter (4th argument, defaults to false)
    • When throttle=false (default): behaves exactly as before - no throttling, immediate callbacks
    • When throttle=true: fires callback immediately on first observation, then throttles subsequent callbacks with rAF
  2. useOverflow - Now uses useResizeObserver with throttle=true for internal optimization
  3. Backwards compatible - Existing consumers get unchanged behavior by default

API

useResizeObserver(
  callback,
  target?,
  depsArray?,
  throttle?  // new - defaults to false for backwards compatibility
)

Expected INP Impact (when throttle=true)

Scenario Before After Improvement
Worst case (rapid window resizing, many observers) ~50-100ms cumulative per resize <16ms (1 frame) 70-85% reduction
Average case (occasional resize, few observers) ~20-40ms per resize <16ms 40-60% reduction
Best case (single resize event) ~10-20ms <16ms ~20% reduction

Why opt-in?

  • Backwards compatibility - Existing consumers get the same behavior they had before
  • Explicit intent - Consumers who want performance gains can opt-in consciously
  • Use-case flexibility - Some consumers may need every resize event (analytics, animations, cumulative calculations)

Why this matters

ResizeObserver can fire many times per frame during animations or rapid resizing:

  • Each callback may trigger state updates → re-renders
  • Without throttling, this causes jank and dropped frames
  • rAF batching ensures maximum 1 update per frame (60fps = 16ms budget)

The "first-immediate" pattern ensures initial render gets correct size without delay.


Part of the INP performance optimization effort. See #7312 for full context.

…useOverflow

- useResizeObserver fires callback immediately on first observation, then throttles with rAF
- useOverflow uses the same pattern to avoid initial flash of incorrect overflow state
- Added isFirstCallback ref pattern to skip throttling on initial mount

Part of #7312
@changeset-bot
Copy link

changeset-bot bot commented Dec 15, 2025

🦋 Changeset detected

Latest commit: 64a0de0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@primer/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added the integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm label Dec 15, 2025
@github-actions
Copy link
Contributor

👋 Hi, this pull request contains changes to the source code that github/github-ui depends on. If you are GitHub staff, test these changes with github/github-ui using the integration workflow. Or, apply the integration-tests: skipped manually label to skip these checks.

return
}
}
setHasOverflow(false)
Copy link
Contributor Author

@mattcosta7 mattcosta7 Dec 15, 2025

Choose a reason for hiding this comment

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

I believe this is a current bug, since things can never go to not overflow states

@mattcosta7 mattcosta7 self-assigned this Dec 15, 2025
@mattcosta7 mattcosta7 marked this pull request as ready for review December 15, 2025 20:55
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds performance optimizations to useResizeObserver and useOverflow hooks by implementing a "first-immediate" throttling pattern. The changes aim to reduce INP (Interaction to Next Paint) scores by batching ResizeObserver callbacks using requestAnimationFrame while ensuring the initial callback fires immediately to prevent UI flash.

Key changes:

  • Immediate execution on first ResizeObserver callback, then rAF throttling for subsequent callbacks
  • Proper cleanup of pending animation frames on component unmount
  • Behavioral fix in useOverflow where overflow state now correctly resets to false

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
packages/react/src/hooks/useResizeObserver.ts Added first-immediate throttling pattern with rAF batching to reduce layout thrashing during rapid resize events
packages/react/src/hooks/useOverflow.ts Applied same throttling pattern and fixed logic to properly reset hasOverflow to false when overflow is removed
.changeset/perf-use-resize-observer.md Documents the performance improvements as a patch-level change

Copy link
Member

@francinelucca francinelucca left a comment

Choose a reason for hiding this comment

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

LGTM! some non-blocking comments

@github-actions github-actions bot requested a deployment to storybook-preview-7335 December 16, 2025 19:22 Abandoned
@github-actions github-actions bot temporarily deployed to storybook-preview-7335 December 16, 2025 19:33 Inactive
@github-actions github-actions bot temporarily deployed to storybook-preview-7335 December 16, 2025 19:44 Inactive
@francinelucca francinelucca added the integration-tests: skipped manually Changes in this PR do not require an integration test label Dec 16, 2025
@siddharthkp siddharthkp removed the integration-tests: skipped manually Changes in this PR do not require an integration test label Dec 17, 2025
@primer-integration
Copy link

👋 Hi from github/github-ui! Your integration PR is ready: https://github.com/github/github-ui/pull/9375

@primer-integration
Copy link

🔬 github-ui Integration Test Results

Check Status Details
CI ✅ Passed View run
Projects (Memex) ✅ Passed View run
VRT ✅ Passed View run

All checks passed! Your integration PR is ready for review.

@mattcosta7 mattcosta7 changed the title perf(hooks): Add first-immediate throttling to useResizeObserver and useOverflow perf(hooks): Add opt-in throttling to useResizeObserver and useOverflow Dec 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

integration-tests: recommended This change needs to be tested for breaking changes. See https://arc.net/l/quote/tdmpakpm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants