From cde53847d9ef6e2cd62bd7c63c90ecdc49cbafbc Mon Sep 17 00:00:00 2001 From: isaaclombardssw Date: Thu, 23 Jul 2026 17:22:21 +1000 Subject: [PATCH] perf: early-return ButtonRow before its layout-measuring hooks ButtonRow ran useResizeObserver and layout reads unconditionally because the empty check lived in the returned JSX, not before the hooks. On the AI for Business Leaders page 13 of 14 button rows have no buttons yet still created a ResizeObserver. Split into an outer ButtonRow that returns null before any hooks when there are no buttons, delegating the measuring logic to a new ButtonRowInner that owns all the hooks and only mounts when buttons exist. Keeps hooks unconditional (Rules of Hooks) and preserves the full-width-button behaviour unchanged. Closes #4900 Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Q8genJyZboXaa4vgYhJF4P --- components/blocksSubtemplates/buttonRow.tsx | 133 ++++++++++---------- 1 file changed, 69 insertions(+), 64 deletions(-) diff --git a/components/blocksSubtemplates/buttonRow.tsx b/components/blocksSubtemplates/buttonRow.tsx index 81e57047e0..8df05d6387 100644 --- a/components/blocksSubtemplates/buttonRow.tsx +++ b/components/blocksSubtemplates/buttonRow.tsx @@ -8,7 +8,16 @@ import React, { useCallback, useRef, useState } from "react"; import { useResizeObserver } from "usehooks-ts"; import { Button } from "../button/templateButton"; +// Outer guard: bail out before any hooks when there's nothing to render, so the +// 13-of-14 button rows on a page that carry no buttons don't create a +// ResizeObserver or read layout. All layout-measuring hooks live in the inner +// component, which only mounts when buttons exist. const ButtonRow = ({ className, data }) => { + if (!(data.buttons?.length > 0)) return null; + return ; +}; + +const ButtonRowInner = ({ className, data }) => { const buttonContainer = useRef(null); const buttonRefs = useRef([]); const [buttonIsFullWidth, setButtonIsFullWidth] = useState(false); @@ -51,72 +60,68 @@ const ButtonRow = ({ className, data }) => { useResizeObserver({ ref: buttonContainer, onResize: measure }); return ( - <> - {data.buttons?.length > 0 && ( -
- {data.buttons?.map((button, index) => { - const buttonElement = ( -
- )} - + return button.buttonLink && !button.leadCaptureFormOption ? ( + isInPageAnchor ? ( + { + e.preventDefault(); + const target = document.getElementById( + button.buttonLink.slice(1) + ); + target?.scrollIntoView({ behavior: "smooth" }); + history.replaceState(null, "", button.buttonLink); + }} + > + {buttonElement} + + ) : ( + + {buttonElement} + + ) + ) : ( + + {buttonElement} + + ); + })} + ); };