Skip to content

FluentOverflow: Add MaxRenderedItems parameter to avoid rendering all children as DOM elements #4949

Description

@JamesNK

Summary

FluentOverflow currently renders all FluentOverflowItem children as DOM elements, then uses JavaScript interop (MutationObserver/ResizeObserver) to detect which ones overflow. When there are hundreds or thousands of items, this forces an expensive browser reflow that can block the UI thread long enough to kill a Blazor Server SignalR connection (~10+ seconds of unresponsive layout).

Problem

In the Aspire Dashboard, we use FluentOverflow to display resource URLs and metric filter tags. Some resources can have hundreds of URLs or filter dimension values. Rendering all of them as DOM elements triggers a forced reflow that's catastrophically expensive — in our case, it disconnects the SignalR connection entirely.

The component only ever displays a handful of items (the ones that fit in the available space), so rendering all items into the DOM just to measure which ones overflow is wasteful when the item count is large.

Current Workaround

We manually cap the number of items passed into ChildContent and track a "pre-overflowed" count to include in the +N badge:

@{
    var maxRenderedUrls = 20;
    var renderedUrls = DisplayedUrls.Take(maxRenderedUrls).ToList();
    var preOverflowedCount = DisplayedUrls.Count - renderedUrls.Count;
}

<FluentOverflow>
    <ChildContent>
        @for (var i = 0; i < renderedUrls.Count; i++)
        {
            <FluentOverflowItem Data="renderedUrls[i]">...</FluentOverflowItem>
        }
    </ChildContent>
    <MoreButtonTemplate Context="overflow">
        <FluentButton>@($"+{overflow.ItemsOverflow.Count() + preOverflowedCount}")</FluentButton>
    </MoreButtonTemplate>
    <OverflowTemplate Context="overflow">
        @* Must manually concat pre-overflowed items into the popup *@
        @foreach (var item in overflow.ItemsOverflow.Select(i => i.Data).Concat(allItems.Skip(maxRenderedUrls)))
        {
            ...
        }
    </OverflowTemplate>
</FluentOverflow>

This works but requires every consumer to duplicate the same capping logic and manually merge pre-overflowed items into templates.

Proposed Solution

Add a MaxRenderedItems parameter (or similar) to FluentOverflow:

/// <summary>
/// Maximum number of items to render as DOM elements for overflow measurement.
/// Items beyond this limit are treated as pre-overflowed without being added to the DOM.
/// When null (default), all items are rendered (current behavior).
/// </summary>
[Parameter]
public int? MaxRenderedItems { get; set; }

When set, the component would:

  1. Only render the first MaxRenderedItems items as FluentOverflowItem DOM elements
  2. Automatically include the remaining items in ItemsOverflow (and thus in MoreButtonTemplate / OverflowTemplate counts and collections)
  3. Preserve the existing Data property on overflow items so consumers can still access the underlying data in templates

This keeps backward compatibility (default is null = render all, same as today) while giving consumers a simple escape hatch for large collections.

Environment

  • Package: Microsoft.FluentUI.AspNetCore.Components 4.14.1
  • Scenario: Blazor Server with SignalR (most sensitive to long UI thread blocks)
  • Scale: 100-500+ items in a single FluentOverflow

Metadata

Metadata

Assignees

No one assigned

    Labels

    closed:doneWork is finished. The version in which the work will show up might not have been released yet!improvementA non-feature-adding improvementstatus:needs-investigationNeeds additional investigationv5For the next major version

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions