From 839b4721fbda018fe6238085af72521172db5d17 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Fri, 14 Aug 2026 11:04:08 +0200 Subject: [PATCH] perf(iframe): lazy-load the iframe widget Also from PR #6582. An iframe is an entire browsing context, so one scrolled off a tall board is the single most expensive thing a widget can load eagerly - and this one had no loading attribute at all. Records what else was mined from that PR and declined, notably its dynamic() for BoardItemMenu: that component already early-returns null outside edit mode, so there is no per-item overlay cost, and dynamic() fetches its chunk when the component renders rather than when it returns something - so the chunk would still load on every board render. It only pays off with the isEditMode guard hoisted to the call site, which is a board-rendering change worth measuring first rather than adopting on faith. --- docs/perf/WIDGET-AUDIT.md | 14 ++++++++++++++ packages/widgets/src/iframe/component.tsx | 3 +++ 2 files changed, 17 insertions(+) diff --git a/docs/perf/WIDGET-AUDIT.md b/docs/perf/WIDGET-AUDIT.md index 6c598e136..bbe4c52bb 100644 --- a/docs/perf/WIDGET-AUDIT.md +++ b/docs/perf/WIDGET-AUDIT.md @@ -55,6 +55,20 @@ panel discards nothing. This also corrects three verdicts in the table below: `coolify` was recorded as "clean (bounded)" and `firewall` and `health-monitoring` as triaged/fixed on other grounds. All three were carrying this cost. +### What else #6582 had, and what was declined + +Mining the rest of that PR's perf-shaped hunks: + +| their change | taken? | +| --- | --- | +| `keepMounted={false}` on 2 accordions | **taken, and extended to 4 sites** — above | +| `loading="lazy"` on the `iframe` widget | **taken** — an iframe is a whole browsing context, so one scrolled off a tall board is the most expensive thing a widget can load eagerly | +| `loading="lazy"` on media-releases and rssFeed | already done here | +| `dynamic()` for `BoardItemMenu` | **declined, and it does not work as written.** `BoardItemMenu` already early-returns `null` outside edit mode, so there is no overlay-per-item cost to begin with — but it is *rendered* unconditionally, and `dynamic()` fetches its chunk when the component renders, not when it returns something. So the chunk still loads on every board render. It would only pay off with the `isEditMode` guard hoisted to the call site, which is a board-rendering change worth measuring first | +| `loading="lazy"` on the immich carousel | declined — the carousel's image *is* the visible content, so there is nothing off-screen to defer | +| `memo()` on `item-content` / `grid-editor` | not extractable — entangled with that PR's new advanced-focus feature, portals and viewport hooks, not a standalone perf change | +| 5 `` boundaries | mostly in that PR's new components; not applicable here | + ## The systemic finding this pass: inactive tab panels render in full **Mantine's `Tabs` keeps inactive panels mounted by default.** In Mantine 9 on React 19 that is diff --git a/packages/widgets/src/iframe/component.tsx b/packages/widgets/src/iframe/component.tsx index ac794a631..24d977c23 100644 --- a/packages/widgets/src/iframe/component.tsx +++ b/packages/widgets/src/iframe/component.tsx @@ -26,6 +26,9 @@ export default function IFrameWidget({ options, isEditMode }: WidgetComponentPro style={isEditMode ? { userSelect: "none", pointerEvents: "none" } : undefined} className={classes.iframe} src={embedUrl} + // An iframe is a whole browsing context, so one scrolled off a tall board is by far the most + // expensive thing a widget can load eagerly. + loading="lazy" title="widget iframe" allow={allowedPermissions} scrolling={allowScrolling ? "yes" : "no"}