Skip to content

feat: add reflow-free element measurement utility#271

Draft
aidenybai wants to merge 4 commits intomainfrom
cursor/element-measurement-utility-0341
Draft

feat: add reflow-free element measurement utility#271
aidenybai wants to merge 4 commits intomainfrom
cursor/element-measurement-utility-0341

Conversation

@aidenybai
Copy link
Copy Markdown
Owner

@aidenybai aidenybai commented Mar 28, 2026

Summary

Adds createElementMeasurer, a utility for measuring DOM elements without triggering synchronous layout reflow. Also removes unused git blame code.

How it works

The utility uses the requestAnimationFrame + IntersectionObserver pattern:

  1. Each animation frame, tracked elements are attached to an IntersectionObserver
  2. The observer fires asynchronously, delivering boundingClientRect data without forcing synchronous layout/paint
  3. Elements are immediately unobserved after measurement
  4. The next frame is scheduled to repeat the cycle
requestAnimationFrame(() => {
  // attach elements to IntersectionObserver
  // observer measures without reflow/paint
  // un-attach and schedule next frame
})

This avoids the performance cost of getBoundingClientRect() and getComputedStyle() which force the browser to flush pending layout work synchronously.

Changes

  • packages/react-grab/src/types.ts — Added ElementMeasurement interface with x, y, width, height, isIntersecting, and intersectionRatio fields
  • packages/react-grab/src/utils/measure-element.ts — New createElementMeasurer factory function that returns an observe/unobserve/disconnect handle
  • packages/react-grab/src/utils/fetch-git-blame.ts — Deleted (unused client-side git blame fetcher)
  • packages/e2e-app/vite-plugin-git-blame.ts — Deleted (unused Vite dev server middleware for git blame)

API

const measurer = createElementMeasurer((element, measurement) => {
  // measurement: { x, y, width, height, isIntersecting, intersectionRatio }
});

measurer.observe(someElement);
measurer.unobserve(someElement);
measurer.disconnect();
Open in Web Open in Cursor 

Summary by cubic

Adds createElementMeasurer to measure DOM elements without triggering layout reflow, improving react-grab performance. Also removes unused git blame utilities.

  • New Features

    • Added createElementMeasurer in packages/react-grab/src/utils/measure-element.ts with observe, unobserve, and disconnect.
    • Uses requestAnimationFrame + IntersectionObserver to read boundingClientRect asynchronously.
    • Added ElementMeasurement in packages/react-grab/src/types.ts with x, y, width, height, isIntersecting, and intersectionRatio.
  • Refactors

    • Cleaned up measurer internals: clearer names, extracted frame cancellation, removed custom thresholds, and inlined the observe step in the rAF callback.
    • Removed unused git blame code: deleted packages/react-grab/src/utils/fetch-git-blame.ts and packages/e2e-app/vite-plugin-git-blame.ts.

Written for commit 26a0208. Summary will update on new commits.

cursoragent and others added 2 commits March 28, 2026 10:29
Introduces createElementMeasurer, a reflow-free measurement utility.
Each frame, it attaches elements to an IntersectionObserver which
delivers boundingClientRect asynchronously without triggering
synchronous layout. Elements are immediately unobserved after
measurement and re-observed on the next animation frame.

- ElementMeasurement interface in types.ts
- createElementMeasurer factory in utils/measure-element.ts

Co-authored-by: Aiden Bai <[email protected]>
@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Mar 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
react-grab-website Ready Ready Preview, Comment Mar 28, 2026 9:03pm

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new bot commented Mar 28, 2026

Open in StackBlitz

npm i https://pkg.pr.new/aidenybai/react-grab/@react-grab/cli@271
npm i https://pkg.pr.new/aidenybai/react-grab/grab@271
npm i https://pkg.pr.new/aidenybai/react-grab@271

commit: 26a0208

- Rename variables for clarity (frameId → pendingFrameId, observer →
  intersectionObserver, isDisconnected → didDisconnect)
- Extract cancelPendingFrame to eliminate repeated cancel pattern (DRY)
- Remove unnecessary threshold array (re-observe each frame always
  triggers an initial entry, default threshold suffices)
- Inline single-use observeTrackedElements into rAF callback
- Rename onMeasure → onMeasurement for consistency
- Rename MeasureElementHandle → ElementMeasurerHandle to match factory

Co-authored-by: Aiden Bai <[email protected]>
Delete fetch-git-blame.ts (client-side fetcher) and
vite-plugin-git-blame.ts (dev server middleware) — neither was
imported or used anywhere in the codebase.

Co-authored-by: Aiden Bai <[email protected]>
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