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

BadgeView

A content-wrapping overlay that pins a small badge — a count, label, or dot — to one of the four corners of a wrapped view. Use it for unread counts, cart counters, “has new” indicators, status markers, or any “look at me” callout that sits on top of an icon, avatar, button, or card.

Setting Text to an empty string (and leaving IsDot false) hides the badge automatically — bind your unread/count value directly and the control shows or clears itself.

  • NuGet downloads for Shiny.Maui.Controls
  • NuGet downloads for Shiny.Blazor.Controls
Frameworks
.NET MAUI
Blazor
BadgeView corner positions with dynamic count and 99+ overflowBadgeView dot indicator, pulse animation, and custom colors
  • Four-corner positioning (TopLeft, TopRight, BottomLeft, BottomRight) with per-corner offset nudge
  • Auto-hide when Text is empty — bind your count source directly and let the control show/clear itself
  • Dot mode (IsDot) for simple “has new” / “has unread” indicators
  • MaxCount numeric overflow rendering (e.g. 127"99+")
  • Scale-in/out animation when the badge appears or disappears (IsAnimated)
  • Optional continuous pulse animation (IsPulsing) for attention-grabbing badges
  • Configurable colors, border, font, corner radius, and padding
  • Blazor honors prefers-reduced-motion and disables animations when set

The child element is the Content (it is the ContentProperty), so nest it directly inside <shiny:BadgeView>.

<shiny:BadgeView xmlns:shiny="http://shiny.net/maui/controls"
Text="{Binding UnreadCount}"
MaxCount="99">
<Border Stroke="#E5E7EB" StrokeThickness="1" Padding="14,10"
StrokeShape="RoundRectangle 10">
<Label Text="📬 Inbox" FontSize="16" />
</Border>
</shiny:BadgeView>

When UnreadCount becomes "" the badge is hidden; when it parses as a number above MaxCount, the badge renders "99+" instead of the raw value.

<shiny:BadgeView Text="9" Position="TopLeft"> ... </shiny:BadgeView>
<shiny:BadgeView Text="9" Position="TopRight"> ... </shiny:BadgeView> <!-- default -->
<shiny:BadgeView Text="9" Position="BottomLeft"> ... </shiny:BadgeView>
<shiny:BadgeView Text="9" Position="BottomRight"> ... </shiny:BadgeView>

OffsetX (default 4) and OffsetY (default -4) nudge the badge from the corner. Positive OffsetX pushes outward; positive OffsetY pushes downward (negative lifts the badge above the corner).

For a “has new” / “has unread” state with no count:

<shiny:BadgeView IsDot="{Binding HasNew}"
BadgeColor="#3B82F6"
OffsetX="2" OffsetY="2">
<Border WidthRequest="56" HeightRequest="56"
StrokeShape="RoundRectangle 28"
Padding="12">
<Label Text="👤" FontSize="22"
HorizontalTextAlignment="Center"
VerticalTextAlignment="Center" />
</Border>
</shiny:BadgeView>

When IsDot is true, Text, MaxCount, and BadgePadding are ignored and the badge renders as a circle of DotSize (default 10).

<shiny:BadgeView Text="NEW"
IsPulsing="True"
BadgeColor="#F59E0B"
FontSize="9">
<Border Padding="14,10" StrokeShape="RoundRectangle 10"
Stroke="#E5E7EB" StrokeThickness="1">
<Label Text="✨ Features" FontSize="16" />
</Border>
</shiny:BadgeView>

IsAnimated (default true) handles the scale-in/out when the badge appears or disappears. IsPulsing (default false) runs a continuous gentle scale animation while the badge is visible — leave it off for normal count badges, turn it on for important “look at me” badges.

<BadgeView Text="@unreadCount"
Position="BadgePosition.TopRight"
MaxCount="99">
<div class="inbox-card">📬 Inbox</div>
</BadgeView>

When unreadCount is "" the badge is hidden.

<BadgeView IsDot="@hasNew"
BadgeColor="#3B82F6"
OffsetX="2" OffsetY="2">
<div class="avatar">👤</div>
</BadgeView>
<BadgeView Text="NEW"
IsPulsing="true"
BadgeColor="#F59E0B"
FontSize="9">
<div class="feature-card">✨ Features</div>
</BadgeView>
PropertyTypeDefaultDescription
ContentView?nullThe wrapped view (ContentProperty)
Textstring""Badge text. Empty hides the badge unless IsDot is true
PositionBadgePositionTopRightTopLeft, TopRight, BottomLeft, BottomRight
BadgeColorColor#DC2626Badge fill color
BadgeTextColorColorWhiteBadge text color
BadgeBorderColorColorWhiteBorder color (default white creates a clean ring against any background)
BadgeBorderThicknessdouble1.5Border thickness
FontSizedouble10Badge text font size
FontAttributesFontAttributesBoldBadge font weight
CornerRadiusdouble999Default fully rounded pill
BadgePaddingThickness6,2Inner padding
OffsetXdouble4Horizontal nudge from the corner (positive = outward)
OffsetYdouble-4Vertical nudge from the corner (negative = upward)
IsDotboolfalseWhen true, renders a small dot — text is ignored
DotSizedouble10Dot diameter when IsDot is true
MaxCountint0When > 0 and Text parses as a number above this limit, renders "{MaxCount}+"
IsAnimatedbooltrueScale/fade in-out when the badge appears or disappears
IsPulsingboolfalseContinuous pulse animation while visible
ParameterTypeDefaultDescription
ChildContentRenderFragment?The wrapped view
Textstring?""Badge text. Empty hides the badge unless IsDot is true
PositionBadgePositionTopRightTopLeft, TopRight, BottomLeft, BottomRight
BadgeColorstring#DC2626Badge fill color (CSS)
BadgeTextColorstring#FFFFFFBadge text color (CSS)
BadgeBorderColorstring#FFFFFFBadge border color (CSS)
BadgeBorderThicknessdouble1.5Border thickness (px)
FontSizedouble10Badge text font size (px)
FontWeightstring”700”Badge text font weight (CSS)
CornerRadiusdouble999Default fully rounded pill (px)
BadgePaddingstring”2px 6px”Inner padding (CSS)
OffsetXdouble4Horizontal nudge from the corner (px)
OffsetYdouble-4Vertical nudge from the corner (px)
IsDotboolfalseRender a small dot — text is ignored
DotSizedouble10Dot diameter (px) when IsDot is true
MaxCountint0When > 0 and Text is numeric above this limit, renders "{MaxCount}+"
IsAnimatedbooltrueCSS scale-in animation on first render
IsPulsingboolfalseContinuous CSS pulse animation while visible
CssClassstring?nullAdditional CSS class on the root
  • Text being an empty string is the auto-hide signal — bind your count source directly and set it to "" (or null) to clear the badge.
  • MaxCount only formats when Text parses as an integer. Non-numeric text ("NEW", "PRO", etc.) is shown as-is regardless of MaxCount.
  • Default OffsetX=4, OffsetY=-4 nudges the badge slightly outside the corner of the wrapped content for the typical “hangs off the edge” badge look. Set both to 0 to keep the badge fully inside the corner instead.
  • BadgeBorderColor defaults to white so the badge reads cleanly against any underlying content; match it to the surrounding background when wrapping inside a colored card.
  • Use IsDot for unread indicators (no count needed) and MaxCount for count badges. Reserve IsPulsing for genuinely important badges — it’s eye-catching by design.
  • The wrapped view is laid out exactly as if the badge weren’t there — the badge does not affect the host’s measurement.
  • For inline status labels (not anchored to a corner of another view), see PillView instead.