Skip to content
Shiny.NET

Tab Headers

Frameworks
.NET MAUI

Every tab in a ShinyTabbedPage (or a bare ShinyTabBar, or a Shell running ShinyTabBarBehavior) is an icon with a label under it. Two settings change that: LabelPosition moves the label, and TabTemplate / HeaderTemplate replace what is drawn inside the tab altogether.

<shiny:ShinyTabbedPage LabelPosition="End">
<shiny:ShinyTabItem Title="Home" Icon="home" />
<shiny:ShinyTabItem Title="Inbox" Icon="message" LabelPosition="Top" />
<shiny:ShinyTabItem Title="Search" Icon="search" LabelPosition="Hidden" />
</shiny:ShinyTabbedPage>

LabelPosition on the page or the bar sets every tab; ShinyTabItem.LabelPosition overrides one.

Value Label
Default On a tab: follow the bar. On the bar: Bottom.
Bottom Under the icon (the default).
Top Above the icon.
Start Beside the icon on the leading side — the left in a left-to-right layout.
End Beside the icon on the trailing side — the right in a left-to-right layout.
Hidden No label on this tab, whatever LabelMode says.

Start and End follow FlowDirection, so in a right-to-left layout Start is on the right. They suit a wide bar on a tablet or desktop. A side label is not truncated, so keep those titles short.

LabelMode still decides whether a label shows (Always, SelectedOnly, Never). LabelPosition only decides where it goes, except for Hidden, which makes a single tab icon-only in an otherwise labelled bar.

Every indicator style keeps working: the pill sits behind the icon, the dot under the whole tab, and the line and underline on the cell’s edges.

TabTemplate (on the page or the bar) replaces the icon, label and badge of every tab with your own layout. ShinyTabItem.HeaderTemplate does the same for one tab and beats the bar’s. A DataTemplateSelector works too, and is handed the ShinyTabItem.

<shiny:ShinyTabbedPage IndicatorStyle="None">
<shiny:ShinyTabbedPage.TabTemplate>
<DataTemplate x:DataType="shiny:TabHeaderContext">
<HorizontalStackLayout Spacing="6">
<!-- the bar's own icon: tinted, animated, motion playback included -->
<ContentView Content="{Binding Icon}" />
<Label Text="{Binding Title}" IsVisible="{Binding IsLabelVisible}">
<Label.Triggers>
<DataTrigger TargetType="Label" Binding="{Binding IsSelected}" Value="True">
<Setter Property="FontAttributes" Value="Bold" />
</DataTrigger>
</Label.Triggers>
</Label>
<Label Text="{Binding Badge}" IsVisible="{Binding HasBadge}" />
</HorizontalStackLayout>
</DataTemplate>
</shiny:ShinyTabbedPage.TabTemplate>
<shiny:ShinyTabItem Title="Home" Icon="home" />
<shiny:ShinyTabItem Title="Profile" Icon="user">
<!-- one tab can have its own -->
<shiny:ShinyTabItem.HeaderTemplate>
<DataTemplate x:DataType="shiny:TabHeaderContext">
<shiny:Avatar Initials="AR" WidthRequest="28" HeightRequest="28" />
</DataTemplate>
</shiny:ShinyTabItem.HeaderTemplate>
</shiny:ShinyTabItem>
</shiny:ShinyTabbedPage>

The bar keeps everything that makes a tab a tab: the tap, selection, the indicator, the overflow More tab, and the automation ids. The template’s children never take the tap, so a Button inside one will not swallow it. The template sits where the icon would, so the pill is drawn behind it. Set IndicatorStyle="None" if your template draws its own selected state.

The template’s BindingContext is a TabHeaderContext, not your view model:

Property Notes
Item The ShinyTabItem — for its Icon, IconImage, Route, Tag, …
Title The resolved label (the page’s ShinyTabs.Title first, then the tab’s).
IsSelected True while this is the selected tab.
IsEnabled Mirrors the tab. The bar already dims a disabled cell.
IsLabelVisible Whether the bar would have shown a label — LabelMode plus Hidden — so a template can follow the bar’s label rules.
LabelPosition The resolved position, never Default.
Color The explicit SelectedColor / UnselectedColor for the current state; null when following the theme.
Badge / HasBadge / IsBadgeDot / BadgeColor The resolved badge. "" is a dot.
Icon The bar’s own realized icon view (MotionIconView or Image), or null. A view has one parent — bind it once.
IsOverflow True for the synthesized More tab, which the bar template draws too.

Icon is the way to keep the bar’s animated motion icons in a custom layout: <ContentView Content="{Binding Icon}" /> gives you the same view, tint and selection animation a default tab has. To draw your own artwork instead, ignore it and read Item.Icon or Item.IconImage.

claude plugin marketplace add shinyorg/skills
claude plugin install shiny@shiny

One plugin installs all 48 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 48 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 shiny-controls Skill