Skip to content
Shiny Controls v1.0 - The Ultra Control Suite for .NET MAUI & BlazorO...M...G!

StaggeredGrid

A masonry (waterfall) grid layout that places items into columns of equal width and lets each item’s height vary naturally, eliminating the uniform-height constraint of standard grids. Native handlers deliver high-performance scrolling on every platform.

  • NuGet downloads for Shiny.Maui.Controls
  • NuGet downloads for Shiny.Blazor.Controls
Frameworks
.NET MAUI
Blazor

MAUI

Two Columns Three Columns
Pinterest masonry layout with two columns Same data re-flowed into three columns at runtime

Blazor

Masonry layout Image gallery
Masonry layout on Blazor Image gallery on Blazor
  • Variable-Height Items – Each item fills its natural height; the next item is placed in the shortest column, producing the classic Pinterest waterfall effect.
  • Configurable Column Count – Set ColumnCount to any integer to control the number of columns.
  • Native Handlers – Android: RecyclerView with StaggeredGridLayoutManager; iOS: custom WaterfallLayout (UICollectionViewLayout); Windows: WaterfallVirtualizingLayout; Blazor: CSS column-count.
  • Empty View – Provide an EmptyViewTemplate / EmptyTemplate to render a placeholder when the data source is empty.
  • Item SelectionItemSelectedCommand (MAUI) and ItemSelected (Blazor) surface tap/click interactions.
claude plugin marketplace add shinyorg/skills
claude plugin install shiny@shiny

One plugin installs all 35 Shiny skills. Your agent loads only the skill relevant to what you're building, so there's no cost to having them all available.

copilot plugin marketplace add https://github.com/shinyorg/skills
copilot plugin install shiny@shiny

One plugin installs all 35 Shiny skills. Your agent loads only the skill relevant to what you're building, so there's no cost to having them all available.

View Skills Repository
<shiny:StaggeredGrid ItemsSource="{Binding Photos}"
ColumnCount="2"
ColumnSpacing="8"
RowSpacing="8"
ItemSelectedCommand="{Binding OpenPhotoCommand}">
<shiny:StaggeredGrid.ItemTemplate>
<DataTemplate>
<Border StrokeShape="{RoundRectangle CornerRadius=8}">
<Image Source="{Binding Url}" Aspect="AspectFill" />
</Border>
</DataTemplate>
</shiny:StaggeredGrid.ItemTemplate>
<shiny:StaggeredGrid.EmptyViewTemplate>
<DataTemplate>
<Label Text="No photos yet" HorizontalOptions="Center" />
</DataTemplate>
</shiny:StaggeredGrid.EmptyViewTemplate>
</shiny:StaggeredGrid>
<StaggeredGrid Items="photos"
ColumnCount="2"
ColumnSpacing="16"
RowSpacing="16"
ItemSelected="OnPhotoSelected">
<ItemTemplate Context="photo">
<div class="photo-card">
<img src="@photo.Url" alt="@photo.Title" />
</div>
</ItemTemplate>
<EmptyTemplate>
<p>No photos yet.</p>
</EmptyTemplate>
</StaggeredGrid>
@code {
List<Photo> photos = [];
void OnPhotoSelected(Photo photo) { }
}
Property MAUI Type Blazor Type Default Description
ItemsSource / Items IEnumerable IReadOnlyList<TItem> Collection of items to display
ItemTemplate DataTemplate RenderFragment<TItem> Template for each grid item
EmptyViewTemplate / EmptyTemplate DataTemplate RenderFragment Content shown when the source is empty
ColumnCount int int 2 Number of masonry columns
ColumnSpacing double double – / 16 Horizontal gap between columns in px
RowSpacing double double – / 16 Vertical gap between items in px

MAUI:

  • ItemSelectedCommand (ICommand) – Fired when a grid item is tapped; parameter is the selected item

Blazor:

  • ItemSelected (EventCallback<TItem>) – Fired when a grid item is clicked/tapped
  • Items are distributed into columns using a shortest-column-first algorithm so column heights stay balanced
  • On iOS, if the root view of the ItemTemplate has an explicit HeightRequest, it is used directly for layout measurement. This is the most reliable approach for items with remote images or async content
  • ColumnCount changes at runtime trigger a full layout pass
  • When ItemsSource / Items is null or empty, EmptyViewTemplate / EmptyTemplate is rendered in its place
  • ColumnSpacing applies between columns; RowSpacing applies between items within a column