Tab Headers
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.
Label position
Section titled “Label position”<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.
Header templates
Section titled “Header templates”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.
Step 1 — Add the marketplace:
claude plugin marketplace add shinyorg/skillsStep 2 — Install the plugin:
claude plugin install shiny@shinyOne 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.
Step 1 — Add the marketplace:
copilot plugin marketplace add https://github.com/shinyorg/skillsStep 2 — Install the plugin:
copilot plugin install shiny@shinyOne 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.


