diff --git a/app/src/components/widgets/IntervalHeatmap.tsx b/app/src/components/widgets/IntervalHeatmap.tsx
index 684d240..802a7fc 100644
--- a/app/src/components/widgets/IntervalHeatmap.tsx
+++ b/app/src/components/widgets/IntervalHeatmap.tsx
@@ -125,26 +125,35 @@ export function IntervalHeatmap({
}
>
- <>
- {/* Peak-demand readout caption (#77): value + when. Hidden if no peak. */}
- {peakReadout && (
-
-
{peakReadout}
+ {/* #150 regression fix: `children` is evaluated EAGERLY by React when
+ IntervalHeatmap renders — BEFORE IntervalWidgetBody decides to show the
+ loading skeleton. During the loading tick useIntervalPayload returns
+ payload === null, so dereferencing payload.grid here would throw an
+ unguarded render-time TypeError and take the WHOLE dashboard down. Gate
+ the populated subtree on `payload &&` so children is falsy until the
+ payload exists; IntervalWidgetBody renders its skeleton/empty meanwhile. */}
+ {payload && (
+ <>
+ {/* Peak-demand readout caption (#77): value + when. Hidden if no peak. */}
+ {peakReadout && (
+
+ {peakReadout}
+
+ )}
+ {/* The grid fills the remaining height. HeatmapViz draws a scalable SVG
+ inside a box of the height we pass; in the fill cell we let it take
+ the flex remainder via a min-h-0 flex-1 wrapper. We pass the
+ SERVER-computed grid + rowLabels (no client re-aggregation). */}
+
+
- )}
- {/* The grid fills the remaining height. HeatmapViz draws a scalable SVG
- inside a box of the height we pass; in the fill cell we let it take
- the flex remainder via a min-h-0 flex-1 wrapper. We pass the
- SERVER-computed grid + rowLabels (no client re-aggregation). */}
-
-
-
- >
+ >
+ )}
);
};