Skip to content
Introducing AI Conversations: Natural Language Interaction for Your Apps! Learn More

SkeletonView

A content-wrapping control that shows animated shimmer placeholders while data is loading — conceptually similar to MAUI’s RefreshView. You wrap your real content and bind IsBusy; while it is true the content is hidden and shimmer placeholders are shown in its place. When IsBusy becomes false, the placeholders disappear and the real content is shown.

The built-in placeholder is a stack of shimmer “lines” (great for text/paragraph content). For anything else, supply a custom placeholder that matches the shape of the content being loaded — SkeletonTemplate on MAUI, SkeletonContent on Blazor.

  • NuGet downloads for Shiny.Maui.Controls
  • NuGet downloads for Shiny.Blazor.Controls
Frameworks
.NET MAUI
Blazor
Default and custom skeleton loadersDefault line skeleton shimmer
  • Wraps a content area and toggles between content and placeholders with IsBusy
  • Built-in line placeholders (configurable count, height, spacing, corner radius, color)
  • Custom placeholder layout via SkeletonTemplate (MAUI) / SkeletonContent (Blazor)
  • Animated shimmer sweep — a translating LinearGradientBrush band on MAUI, an animated CSS gradient on Blazor
  • Shimmer honors prefers-reduced-motion on Blazor
  • Shimmer can be disabled for static placeholders (ShimmerEnabled)

The child element is the Content (it is the ContentProperty), so you can nest it directly — no need to write <shiny:SkeletonView.Content> unless you also set SkeletonTemplate.

<shiny:SkeletonView xmlns:shiny="http://shiny.net/maui/controls"
IsBusy="{Binding IsBusy}"
ItemCount="4">
<VerticalStackLayout Spacing="8">
<Label Text="Loaded Article" FontSize="16" FontAttributes="Bold" />
<Label Text="This real content appears once loading finishes." />
</VerticalStackLayout>
</shiny:SkeletonView>

MAUI — Custom Placeholder (match the content shape)

Section titled “MAUI — Custom Placeholder (match the content shape)”
<shiny:SkeletonView IsBusy="{Binding IsBusy}">
<shiny:SkeletonView.SkeletonTemplate>
<DataTemplate>
<HorizontalStackLayout Spacing="12">
<BoxView WidthRequest="56" HeightRequest="56" CornerRadius="28" Color="#E1E1E6" />
<VerticalStackLayout Spacing="10" VerticalOptions="Center" WidthRequest="200">
<BoxView HeightRequest="14" CornerRadius="6" Color="#E1E1E6" HorizontalOptions="Fill" />
<BoxView HeightRequest="14" CornerRadius="6" Color="#E1E1E6" WidthRequest="120" HorizontalOptions="Start" />
</VerticalStackLayout>
</HorizontalStackLayout>
</DataTemplate>
</shiny:SkeletonView.SkeletonTemplate>
<shiny:SkeletonView.Content>
<HorizontalStackLayout Spacing="12">
<BoxView WidthRequest="56" HeightRequest="56" CornerRadius="28" Color="#7C3AED" />
<VerticalStackLayout Spacing="4" VerticalOptions="Center">
<Label Text="Allan Ritchie" FontSize="16" FontAttributes="Bold" />
<Label Text="Shiny Controls maintainer" TextColor="#6B7280" />
</VerticalStackLayout>
</HorizontalStackLayout>
</shiny:SkeletonView.Content>
</shiny:SkeletonView>

Use BaseColor for the placeholder fill in your SkeletonTemplate so it matches the built-in look.

Blazor — Default Placeholder (text lines)

Section titled “Blazor — Default Placeholder (text lines)”
<SkeletonView IsBusy="@isBusy" ItemCount="4">
<ChildContent>
<h3>Loaded Article</h3>
<p>This real content appears once loading finishes.</p>
</ChildContent>
</SkeletonView>

Add the shiny-skeleton__shape class to any element in SkeletonContent to give it the shimmer. The CSS custom properties for the colors/duration are inherited from the root, so your shapes shimmer automatically.

<SkeletonView IsBusy="@isBusy">
<SkeletonContent>
<div style="display:flex; gap:12px; align-items:center;">
<div class="shiny-skeleton__shape" style="width:56px; height:56px; border-radius:50%;"></div>
<div style="display:flex; flex-direction:column; gap:10px; flex:1;">
<div class="shiny-skeleton__shape" style="height:14px; border-radius:6px; width:60%;"></div>
<div class="shiny-skeleton__shape" style="height:14px; border-radius:6px; width:40%;"></div>
</div>
</div>
</SkeletonContent>
<ChildContent>
<!-- real content -->
</ChildContent>
</SkeletonView>
PropertyTypeDefaultDescription
ContentView?nullThe real content shown when IsBusy is false (the ContentProperty)
IsBusyboolfalseWhen true, hides content and shows animated placeholders
SkeletonTemplateDataTemplate?nullCustom placeholder layout; when null, built-in lines are used
ItemCountint3Number of built-in placeholder lines (last line is shortened)
BaseColorColor#E1E1E6Fill color of the built-in placeholder shapes
ShimmerColorColorrgba(255,255,255,0.6)Color of the sweeping highlight
ShimmerEnabledbooltrueWhen false, placeholders are static (no sweep)
AnimationDurationuint1200Duration (ms) of a single shimmer sweep
CornerRadiusdouble6Corner radius of the built-in placeholder lines
ItemHeightdouble16Height of each built-in placeholder line
ItemSpacingdouble12Vertical spacing between built-in placeholder lines
ParameterTypeDefaultDescription
IsBusyboolfalseWhen true, hides content and shows placeholders
ChildContentRenderFragment?The real content shown when IsBusy is false
SkeletonContentRenderFragment?Custom placeholder markup; when null, built-in lines are used
ItemCountint3Number of built-in placeholder lines
ItemHeightdouble16Height (px) of each built-in placeholder line
ItemSpacingdouble12Vertical spacing (px) between built-in lines
CornerRadiusdouble6Corner radius (px) of the built-in placeholder lines
BaseColorstring#e1e1e6Base fill color of placeholder shapes
HighlightColorstringrgba(255,255,255,0.6)Color of the sweeping highlight
AnimationDurationdouble1.4Duration (seconds) of a single shimmer sweep
ShimmerEnabledbooltrueWhen false, placeholders are static (no sweep)
CssClassstring?nullAdditional CSS class on the root
  • SkeletonView swaps between content and placeholders by visibility, so the control’s height follows whichever is shown — size your placeholder to roughly match the loaded content to avoid layout jumps.
  • For a full-screen “loading the whole page” experience use Overlay & LoadingOverlay instead — SkeletonView is for inline content regions.
  • Both hosts mirror the same API: IsBusy, a custom placeholder slot, item count/height/spacing, base/highlight colors, animation duration, and a shimmer on/off toggle.