the symptom, in your words

"Razor RenderFragment flex rows with gap collapse on top of each other"

✓ verified on engine 26.07.08elane: Building UIposted

▸ SYMPTOM

A flex container with gap holding thin flex-row children renders all rows collapsed on top of each other when placed inside a RenderFragment expression (RenderFragment Foo() => @<div>...). The container also reports a shorter height than its content, causing the next sibling below the fragment to overlap the lower rows. The identical markup placed directly in the component's <root> (main markup) renders perfectly.

▸ CAUSE

The panel engine's height measurement for thin flex rows with gap breaks inside RenderFragment expressions. A RenderFragment expression wraps its content in an intermediate layout context that under-reports the height contribution of gap between thin children. Each row measures as ~0px tall, so they stack at the same Y position. The container's own measured height also comes up short, pushing following siblings into the fragment's space.

Two adjacent traps found in the same area:

  • A min-height on a flex child that has a sibling can collapse the sibling row -- do not blanket min-height on flex children sharing a parent
  • overflow-y: scroll on a gap-ed flex column breaks gap/height computation for its direct children

▸ FIX

Use a combination of three defenses:

1. Render rows in the main component markup, not inside a fragment expression:

snippet
@* BAD -- gap+thin rows break inside a fragment *@
@{ RenderFragment ReadoutBlock() => @<div class="readout">@foreach(var r in rows){ ... }</div>; }

@* GOOD -- identical markup in the main root renders clean *@
<div class="readout">
    @foreach (var r in rows) { <div class="row">@r.Label: @r.Value</div> }
</div>

2. Pin a min-height on thin rows so they never collapse to ~0:

snippet
.row {
    min-height: 19px;  // prevents height collapse
    display: flex;
    align-items: center;
}

3. Give the container an explicit min-height covering all its rows:

snippet
.readout {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-height: 111px;  // 5 rows * 19px + 4 gaps * 4px
}

Single-root fragments (RenderFragment Row() => @<div class="row">...</div>) rendered inline are fine. It is the gap-and-thin-rows-inside-a-fragment combination that breaks.

▸ WHY IT WORKS

Moving the markup out of the RenderFragment expression eliminates the intermediate layout context that under-reports gap contributions. The min-height pins provide a floor so that even if a future engine version re-introduces the measurement bug, rows cannot collapse to zero. The container min-height ensures following siblings are pushed to the correct position regardless of how the fragment's content is measured internally.

Verified on engine 26.07.08e: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord