From c4b6baf32f10de6f6645ef701b36bdbe6c0915e4 Mon Sep 17 00:00:00 2001 From: Matthew Costabile Date: Mon, 15 Dec 2025 14:59:29 +0000 Subject: [PATCH] perf(UnderlineNav): Batch layout reads and add comment about ResizeObserver throttling - UnderlineNavItem: Batch getBoundingClientRect and getComputedStyle reads - UnderlineNav: Add comment noting ResizeObserver callbacks are now throttled Part of #7312 --- .changeset/perf-underlinenav-item-observer.md | 8 ++++++++ packages/react/src/UnderlineNav/UnderlineNav.tsx | 1 + .../react/src/UnderlineNav/UnderlineNavItem.tsx | 14 +++++++++----- 3 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 .changeset/perf-underlinenav-item-observer.md diff --git a/.changeset/perf-underlinenav-item-observer.md b/.changeset/perf-underlinenav-item-observer.md new file mode 100644 index 00000000000..fa98aa6eae5 --- /dev/null +++ b/.changeset/perf-underlinenav-item-observer.md @@ -0,0 +1,8 @@ +--- +'@primer/react': patch +--- + +perf(UnderlineNav): Batch layout reads and add comment about ResizeObserver throttling + +- UnderlineNavItem: Batch getBoundingClientRect and getComputedStyle reads +- UnderlineNav: Add comment noting ResizeObserver callbacks are now throttled diff --git a/packages/react/src/UnderlineNav/UnderlineNav.tsx b/packages/react/src/UnderlineNav/UnderlineNav.tsx index c0a252d3ffe..58ad30d2aaa 100644 --- a/packages/react/src/UnderlineNav/UnderlineNav.tsx +++ b/packages/react/src/UnderlineNav/UnderlineNav.tsx @@ -291,6 +291,7 @@ export const UnderlineNav = forwardRef( useOnOutsideClick({onClickOutside: closeOverlay, containerRef, ignoreClickRefs: [moreMenuBtnRef]}) + // ResizeObserver callbacks are now throttled with rAF for better INP useResizeObserver((resizeObserverEntries: ResizeObserverEntry[]) => { const navWidth = resizeObserverEntries[0].contentRect.width const moreMenuWidth = moreMenuRef.current?.getBoundingClientRect().width ?? 0 diff --git a/packages/react/src/UnderlineNav/UnderlineNavItem.tsx b/packages/react/src/UnderlineNav/UnderlineNavItem.tsx index 3341f677e11..29c6750a209 100644 --- a/packages/react/src/UnderlineNav/UnderlineNavItem.tsx +++ b/packages/react/src/UnderlineNav/UnderlineNavItem.tsx @@ -91,11 +91,15 @@ export const UnderlineNavItem = forwardRef( ) as HTMLElement const text = content.textContent as string - const iconWidthWithMargin = icon - ? icon.getBoundingClientRect().width + - Number(getComputedStyle(icon).marginRight.slice(0, -2)) + - Number(getComputedStyle(icon).marginLeft.slice(0, -2)) - : 0 + let iconWidthWithMargin = 0 + if (icon) { + // Batch all layout reads: getBoundingClientRect and getComputedStyle together + const iconRect = icon.getBoundingClientRect() + const iconStyle = getComputedStyle(icon) + // Use || 0 fallback for edge cases where margin might be 'auto' or non-numeric + iconWidthWithMargin = + iconRect.width + (parseFloat(iconStyle.marginRight) || 0) + (parseFloat(iconStyle.marginLeft) || 0) + } setChildrenWidth({text, width: domRect.width}) setNoIconChildrenWidth({text, width: domRect.width - iconWidthWithMargin})