Complete list of available ShellUI Native components. Examples assume
xmlns:ui="clr-namespace:YourApp.Components.UI" on the page.
Every component takes its colors from theme tokens mirroring ShellUI's CSS variables
(--background, --primary, --border, …). The shell utility (installed by
shellui-native init) defines a light and a dark palette in ShellTheme and publishes the
active one as application resources, so switching theme repaints every component.
| Token | Role |
|---|---|
Background / Foreground |
Page and dialog surface, body text |
Card / CardForeground |
Card surface |
Popover / PopoverForeground |
Dropdown, popover and select panels |
Primary / PrimaryForeground |
Default button, checked checkbox/switch/radio, progress fill |
Secondary, Muted, Accent (+ *Foreground) |
Secondary button, tab list and skeleton, hover backgrounds |
Destructive, Success, Warning, Info (+ *Foreground) |
Status variants (badge, alert, progress, labels) |
Border, Input, Ring |
Dividers, field borders, focus ring |
Overlay |
Dialog / drawer / sheet backdrop |
Use the tokens in your own XAML — each token is published as a Color (ShellUI<Token>) and a
Brush (ShellUI<Token>Brush):
<ContentPage BackgroundColor="{DynamicResource ShellUIBackground}">
<Label Text="Muted text" TextColor="{DynamicResource ShellUIMutedForeground}" />
<Border Stroke="{DynamicResource ShellUIBorderBrush}" />
</ContentPage>Initialize early so page-level DynamicResources resolve on first load (components also do it
on first use):
public App()
{
InitializeComponent();
Components.UI.ShellTheme.EnsureInitialized();
}Switch theme with ShellTheme.SetTheme(AppTheme.Dark), ShellTheme.ToggleTheme(), or the
ThemeToggle component. The theme follows the OS setting until you set one.
Customize tokens before the first page loads, then re-publish:
ShellTheme.Light[ShellToken.Primary] = Color.FromArgb("#2563EB");
ShellTheme.Dark[ShellToken.Primary] = Color.FromArgb("#3B82F6");
ShellTheme.Apply();In code, bind any Color/Brush property to a token with the Token extension:
myBorder.Token(Border.StrokeProperty, ShellToken.Border);
Android system bars (opt-in). Set ShellTheme.SyncSystemBars = true before
EnsureInitialized() to make the status and navigation bars follow the theme: the bars turn
transparent over the edge-to-edge page, the page layer draws a Background-colored strip behind
the status bar (so dialog backdrops and sheets cover it), and the bar icons flip with light/dark.
Also set colorPrimary / colorPrimaryDark in Platforms/Android/Resources/values/colors.xml to
your background so the splash-to-app transition doesn't flash the template's purple.
Ported so far: shell, icon, theme-toggle, P0 (button, input, label, checkbox,
switch, card and its parts, separator, badge, progress, alert), P1 (dialog,
drawer, sheet, dropdown, popover and their parts) and P2 (textarea, select, slider,
radio-group, calendar, date-picker, time-picker). Not yet published to NuGet. In an
Avalonia project, shellui-native list shows only these.
Overlays use Avalonia's Popup in the window's overlay layer: the content stays in the logical
tree (theme, DataContext and FindParentOfType work as declared), dropdowns and popovers get
Avalonia's placement and flip above the trigger when there is no room below, and a click outside
closes them. Escape closes the overlay on top. Keyboard focus is not trapped inside an open
dialog yet: Tab can still reach controls behind it.
Setup after shellui-native init: call Components.UI.ShellTheme.EnsureInitialized(); in
App.Initialize after AvaloniaXamlLoader.Load(this);, and add
xmlns:ui="using:YourApp.Components.UI" to your XAML. Switch theme with
ShellTheme.SetTheme(ThemeVariant.Dark) or ShellTheme.ToggleTheme().
Same names, properties and variants as MAUI, except:
| MAUI | Avalonia | |
|---|---|---|
| Icon | <ui:Icon Name="Search" /> |
<ui:Icon Kind="Search" /> (every Avalonia control already has a Name) |
| Input length limit | MaxLength, default int.MaxValue |
MaxLength, 0 (default) means no limit |
| Card children | XAML children | XAML children, collected in Card.Items |
| Block button | HorizontalOptions="Fill" |
HorizontalAlignment="Stretch" |
| Focus ring | Platform focus visuals | shadcn's ring, for keyboard focus only |
| Overlay children | XAML children | XAML children, collected in Items (Dialog, DialogContent, Dropdown, …) |
| Dropdown / Popover | ToggleAsync() / CloseAsync() kept for older code |
Toggle() / Close() only |
| Flipped popup gap | 4px above the trigger | Flush with the trigger (Avalonia doesn't mirror the offset) |
| Slider | Platform slider tinted with tokens | Custom-drawn shadcn slider; adds Step for the arrow keys; ValueChanged passes the new double |
DatePicker DateChanged |
DateChangedEventArgs |
The new DateTime |
Stroke icons from ShellIcons (Lucide 0.475.0 — the set ShellUI
uses), drawn with MAUI shapes: no icon font or package, crisp at any size, theme-aware. The
template ships a curated set of ~110 icons with exact Lucide names (trash-2 → Trash2).
To include more, add their names to ICONS in scripts/generate-icons.py, then run it and
scripts/sync-templates.py.
shellui-native add icon| Property | Type | Default | Description |
|---|---|---|---|
| Name | IconName | None | Which icon (Check, X, Plus, ChevronDown, Search, Settings, Trash2, Sun, Moon, Info, CircleAlert, CircleCheck, TriangleAlert, User, Bell, … see IconName) |
| Size | double | 16 | Width and height in DIPs; the stroke scales with it |
| StrokeWidth | double | 2 | Stroke width on Lucide's 24×24 grid |
| Token | ShellToken | Foreground | Theme color |
| Color | Color? | null | Explicit color (overrides Token) |
<ui:Icon Name="Search" Size="16" Token="MutedForeground" />Interactive button with variants, sizes, an optional icon and a loading state. Sizes to its
content (set HorizontalOptions="Fill" for a full-width button).
shellui-native add buttonVariants: Default, Secondary, Outline, Destructive, Ghost, Link · Sizes: Sm (36), Default (40), Lg (44), Icon (40×40)
| Property | Type | Default | Description |
|---|---|---|---|
| Text | string | "" | Button text |
| Variant | ButtonVariant | Default | Visual style |
| Size | ButtonSize | Default | Height / padding |
| Icon | IconName | None | Optional icon |
| IconPosition | IconPosition | Left | Left or Right of the text |
| IsLoading | bool | false | Shows a spinner and ignores clicks |
| IsEnabled | bool | true | Disabled buttons render at 50% opacity |
<ui:Button Text="Save" Clicked="OnSave" />
<ui:Button Text="New item" Icon="Plus" />
<ui:Button Text="Continue" Icon="ArrowRight" IconPosition="Right" Variant="Outline" />
<ui:Button Icon="Settings" Size="Icon" Variant="Ghost" />Events: Clicked. On Windows buttons are keyboard tab stops (Enter/Space activate).
Two-state button that stays pressed — transparent (or outlined) when off, Accent when on.
shellui-native add toggleProperties: IsPressed (two-way), Text, Icon, Variant (Default, Outline),
Size (Sm 36, Default 40, Lg 44) · Events: PressedChanged
<ui:Toggle Icon="Bookmark" Variant="Outline" IsPressed="{Binding Saved}" />
<ui:Toggle Text="Notify me" Icon="Bell" Size="Sm" PressedChanged="OnToggled" />A row of toggles that work as one control: pick one (the default) or several (Multiple).
shellui-native add toggle-groupProperties (ToggleGroup): Value (two-way; the pressed item, null when none), Multiple,
Values (two-way; the pressed items in Multiple mode), Variant (Default, Outline) ·
Events: ValueChanged, ValuesChanged
Properties (ToggleGroupItem): Value, Text, Icon
<ui:ToggleGroup Value="{Binding View}">
<ui:ToggleGroupItem Value="day" Text="Day" />
<ui:ToggleGroupItem Value="week" Text="Week" />
<ui:ToggleGroupItem Value="month" Text="Month" />
</ui:ToggleGroup>In single mode, tapping the pressed item clears the selection. To always keep one pressed, set
Value back in ValueChanged when it arrives as null.
Single-line text field — 40px, one themed border (the platform control's own frame is removed), ring color + soft glow on focus, destructive border on error.
shellui-native add input| Property | Type | Default | Description |
|---|---|---|---|
| Text | string | "" | Value (two-way) |
| Placeholder | string | "" | Placeholder text |
| IsPassword | bool | false | Mask input |
| HasError | bool | false | Error state |
| IsReadOnly | bool | false | Prevent editing |
| MaxLength | int | int.MaxValue | Maximum characters |
| Keyboard | Keyboard | Default | Soft keyboard type |
<ui:Input Placeholder="you@example.com" Keyboard="Email" Text="{Binding Email}" />
<ui:Input Placeholder="Password" IsPassword="True" />
<ui:Input Text="taken-name" HasError="True" />Events: TextChanged, Completed
One-time-code input: a row of 40px slots over one hidden text field, so paste, autofill and the platform keyboard all work. The active slot shows the ring color and a blinking caret.
shellui-native add input-otpProperties: Length (6), Value (two-way), IsNumeric (true — digits and numeric keyboard),
HasError · Events: ValueChanged, Completed (every slot filled)
<ui:InputOtp Length="6" Value="{Binding Code}" Completed="OnCodeEntered" />Number field with − and + buttons. The value is clamped to Minimum / Maximum, and the buttons
dim at the limits. Typing is committed on Enter or when the field loses focus; text that isn't a
number puts the last value back.
shellui-native add number-inputProperties: Value (decimal?, two-way; null when empty), Minimum, Maximum, Step (1),
Placeholder · Events: ValueChanged
<ui:NumberInput Value="{Binding Quantity}" Minimum="1" Maximum="10" WidthRequest="160" />Text field that turns entries into removable chips. Enter, a comma or a semicolon commits what was
typed (so pasting a, b, c adds three tags); the × on a chip removes it.
shellui-native add tag-input # also installs wrap-layoutProperties: Tags (IList<string>, two-way), Placeholder, MaxTags (0 = no limit),
AllowDuplicates · Events: TagsChanged · Methods: Add(tag), Remove(tag)
<ui:TagInput Tags="{Binding Labels}" Placeholder="Add a label..." MaxTags="6" />Multi-line text field — min 80px, grows with content.
shellui-native add textareaProperties: Text (two-way), Placeholder, MaxLength, HasError · Events: TextChanged
<ui:Textarea Placeholder="Type your message here." Text="{Binding Body}" />16×16 box with a check icon; the whole row (box + label) is the hit target.
shellui-native add checkboxProperties: IsChecked (two-way), Label, HasError, IsEnabled · Events: CheckedChanged
<ui:Checkbox Label="Accept terms and conditions" IsChecked="{Binding Accepted}" />44×24 track with a 20px thumb that slides inside it.
shellui-native add switchProperties: IsToggled (two-way), Label, IsEnabled · Events: Toggled
<ui:Switch Label="Airplane mode" IsToggled="{Binding AirplaneMode}" />Single choice. The group drives its items.
shellui-native add radio-groupProperties (RadioGroup): Value (two-way — the selected item's Value) · Events: ValueChanged
Properties (RadioGroupItem): Value, Text
<ui:RadioGroup Value="{Binding Density}">
<ui:RadioGroupItem Value="default" Text="Default" />
<ui:RadioGroupItem Value="comfortable" Text="Comfortable" />
<ui:RadioGroupItem Value="compact" Text="Compact" />
</ui:RadioGroup>Custom-drawn select: a 40px trigger with a chevrons icon and a floating list with a check on the selected item. Looks the same on every platform.
shellui-native add select| Property | Type | Default | Description |
|---|---|---|---|
| ItemsSource | IList<string> | null | Options |
| SelectedIndex | int | -1 | Selected option (two-way) |
| SelectedItem | string? | — | Read-only selected option |
| Placeholder | string | "Select..." | Shown when nothing is selected |
<ui:Select Placeholder="Select a country" ItemsSource="{Binding Countries}"
SelectedIndex="{Binding CountryIndex}" WidthRequest="280" HorizontalOptions="Start" />Events: SelectedIndexChanged
A Select with a search field: the floating panel has a filter input above the option list, a check on the selected option and a "no results" message. On desktop the search field takes focus when the panel opens, and Enter picks the first match.
shellui-native add combobox| Property | Type | Default | Description |
|---|---|---|---|
| ItemsSource | IList<string> | null | Options |
| Value | string | "" | Selected option (two-way) |
| Placeholder | string | "Select..." | Shown when nothing is selected |
| SearchPlaceholder | string | "Search..." | Placeholder of the filter field |
| EmptyText | string | "No results found." | Shown when nothing matches |
<ui:Combobox Placeholder="Select framework..." ItemsSource="{Binding Frameworks}"
Value="{Binding Framework}" WidthRequest="280" HorizontalOptions="Start" />Events: ValueChanged
Like Combobox, for several options: the trigger shows the picked options as removable chips, and the floating panel has a search field and a checked list. Picking toggles an option and leaves the panel open; click outside, press Escape or (on Android) go back to close it.
shellui-native add multi-select # also installs wrap-layout| Property | Type | Default | Description |
|---|---|---|---|
| ItemsSource | IList<string> | null | Options |
| Values | IList<string> | null | Picked options (two-way) |
| Placeholder | string | "Select..." | Shown when nothing is picked |
| SearchPlaceholder | string | "Search..." | Placeholder of the filter field |
| EmptyText | string | "No results found." | Shown when nothing matches |
<ui:MultiSelect Placeholder="Select frameworks..." ItemsSource="{Binding Frameworks}"
Values="{Binding Picked}" />Events: ValuesChanged · Methods: Toggle(option)
Platform slider tinted with the theme (primary range and thumb, secondary track).
shellui-native add sliderProperties: Value (two-way), Minimum, Maximum · Events: ValueChanged
Month grid with previous/next navigation. The selected day is primary, today is accented, and month names, weekday names and the first day of the week follow the current culture.
shellui-native add calendarProperties: SelectedDate (DateTime?, two-way), DisplayMonth, MinimumDate, MaximumDate · Events: DateSelected
<ui:Calendar SelectedDate="{Binding Day}" DateSelected="OnDay" />Custom-drawn: a 40px trigger (calendar icon + formatted date) that opens a Calendar floating
over the page. Looks the same on every platform.
shellui-native add date-picker # also installs calendarProperties: Date (two-way), MinimumDate, MaximumDate, Format (.NET date format, default MMMM d, yyyy) · Events: DateChanged
<ui:DatePicker Date="{Binding DueDate}" Format="MMM d, yyyy" />Custom-drawn: a 40px trigger (clock icon + formatted time) that opens scrollable hour and minute
columns — plus AM/PM on a 12-hour clock — floating over the page. Picking a value updates Time
straight away; click outside or press Escape to close. Looks the same on every platform.
shellui-native add time-pickerProperties: Time (two-way), MinuteStep (default 5), Is24Hour (defaults to the current
culture's clock), Format (.NET time format; default HH:mm or h:mm tt) · Events: TimeChanged
<ui:TimePicker Time="{Binding StartsAt}" MinuteStep="15" />Typography with size, weight and color variants.
shellui-native add labelProperties: Text, Size (Xs…Xxxl), Weight (Light…Bold), Variant (Default, Muted, Destructive, Success, Warning)
<ui:ShellLabel Text="This username is already taken." Size="Sm" Variant="Destructive" />36×36 outline icon button that switches light/dark; the sun and moon cross-fade.
shellui-native add theme-toggleEvents: ThemeChanged(bool isDark) · Methods: Toggle()
Keeps its content at a fixed width / height ratio: the height follows the width.
shellui-native add aspect-ratioProperties: Ratio (width divided by height; default 16:9 = 1.7778)
<ui:AspectRatio Ratio="1.7778">
<Image Source="cover.jpg" Aspect="AspectFill" />
</ui:AspectRatio>flex flex-wrap gap-2: children flow left to right and wrap onto new rows, centered vertically
within their row. Use it for button rows, chips and tag lists.
shellui-native add wrap-layoutProperties: Spacing (8, between children), LineSpacing (8, between rows), LastChildFill
(the last child takes the rest of its row), MinimumLastChildWidth (96)
<ui:WrapLayout>
<ui:Button Text="One" />
<ui:Button Text="Two" Variant="Outline" />
</ui:WrapLayout>Prefer it to FlexLayout for simple wrapping: it measures each child once per layout pass.
rounded-xl border bg-card shadow-sm container.
shellui-native add card # also installs card-header, card-content, card-footerProperties (Card): Variant (Default, Elevated), IsPressable · Events: Clicked (when pressable)
CardHeader: Title, Description · CardContent: any content · CardFooter: actions, right-aligned
<ui:Card WidthRequest="380" HorizontalOptions="Start">
<ui:CardHeader Title="Create project" Description="Deploy your new project in one click." />
<ui:CardContent><ui:Input Placeholder="Name of your project" /></ui:CardContent>
<ui:CardFooter>
<ui:Button Text="Cancel" Variant="Outline" />
<ui:Button Text="Deploy" />
</ui:CardFooter>
</ui:Card>1px divider in the Border token. Properties: Orientation (Horizontal, Vertical)
Expand/collapse with a height animation. The trigger can wrap a Button or any view.
shellui-native add collapsibleProperties: Open (two-way; also SetOpen(bool), Toggle()) · Events: OpenChanged
<ui:Collapsible>
<Grid ColumnDefinitions="*,Auto">
<Label Text="@peduarte starred 3 repositories" />
<ui:CollapsibleTrigger Grid.Column="1">
<ui:Button Icon="ChevronsUpDown" Size="Icon" Variant="Ghost" />
</ui:CollapsibleTrigger>
</Grid>
<ui:CollapsibleContent>
<Label Text="Revealed when open." />
</ui:CollapsibleContent>
</ui:Collapsible>Stacked sections with dividers and a chevron that rotates when open; content expands and collapses its height.
shellui-native add accordionProperties (Accordion): Type (Single, Multiple), Value (initially open item; comma-separated for Multiple)
Events (Accordion): ItemToggled(value, isOpen)
AccordionItem: Value · AccordionTrigger: Text, or any view as content
<ui:Accordion Type="Single" Value="item-1">
<ui:AccordionItem Value="item-1">
<ui:AccordionTrigger Text="Is it accessible?" />
<ui:AccordionContent><Label Text="Yes." /></ui:AccordionContent>
</ui:AccordionItem>
<ui:AccordionItem Value="item-2">
<ui:AccordionTrigger Text="Is it animated?" />
<ui:AccordionContent><Label Text="Yes." /></ui:AccordionContent>
</ui:AccordionItem>
</ui:Accordion>ScrollView wrapper. Put it in a bordered Border for the shadcn look.
<Border Stroke="{DynamicResource ShellUIBorderBrush}" StrokeShape="RoundRectangle 6">
<ui:ScrollArea HeightRequest="200"><VerticalStackLayout>...</VerticalStackLayout></ui:ScrollArea>
</Border>A muted pill-shaped list; the active tab is raised on the background color. Panels fade in on switch.
shellui-native add tabsTabs: Value (active tab, two-way) · Events: ValueChanged(old, new)
TabsTrigger: Value, Text · TabsContent: Value
<ui:Tabs Value="account">
<ui:TabsList>
<ui:TabsTrigger Value="account" Text="Account" />
<ui:TabsTrigger Value="password" Text="Password" />
</ui:TabsList>
<ui:TabsContent Value="account">...</ui:TabsContent>
<ui:TabsContent Value="password">...</ui:TabsContent>
</ui:Tabs>Trail with chevron separators; links turn foreground on hover.
BreadcrumbItem: Text, IsCurrent · Events: Clicked (not raised for the current item)
<ui:Breadcrumb>
<ui:BreadcrumbItem Text="Home" Clicked="OnCrumb" />
<ui:BreadcrumbItem Text="Components" Clicked="OnCrumb" />
<ui:BreadcrumbItem Text="Breadcrumb" IsCurrent="True" />
</ui:Breadcrumb>Tree of expandable rows with selection. Tapping a row selects it and, when it has children,
expands or collapses it. Nested TreeViewItems are the children.
shellui-native add tree-viewProperties (TreeView): SelectedValue (two-way) · Events: SelectedValueChanged
Properties (TreeViewItem): Text, Value (defaults to Text), Icon, IsExpanded (two-way)
<ui:TreeView SelectedValue="{Binding Path}">
<ui:TreeViewItem Text="src" Icon="Folder" IsExpanded="True">
<ui:TreeViewItem Text="Components" Icon="Folder">
<ui:TreeViewItem Text="Button.cs" Icon="File" />
</ui:TreeViewItem>
<ui:TreeViewItem Text="App.xaml" Icon="FileText" />
</ui:TreeViewItem>
</ui:TreeView>Tappable card with an optional icon, a title, a description and an arrow. Tapping raises
Clicked and opens Url in the system browser when one is set.
shellui-native add link-cardProperties: Title, Description, Icon, Url · Events: Clicked
<ui:LinkCard Title="Documentation" Description="Guides and the component reference."
Icon="FileText" Url="https://example.com/docs" />Step-by-step flow: numbered circles joined by lines (the active one filled, completed ones checked), the active step's content, and Previous / Next / Confirm buttons. A step becomes clickable once it has been reached.
shellui-native add stepperProperties (Stepper): CurrentStep (two-way, 0-based), ShowNavigation (true),
PreviousText, NextText, ConfirmText · Events: CurrentStepChanged, Confirmed ·
Methods: Next(), Previous()
Properties (StepperStep): Title, Description · its child view is the step's content
<ui:Stepper Confirmed="OnDone">
<ui:StepperStep Title="Account" Description="Your details">
<ui:Input Placeholder="you@example.com" />
</ui:StepperStep>
<ui:StepperStep Title="Plan"> ... </ui:StepperStep>
<ui:StepperStep Title="Review"> ... </ui:StepperStep>
</ui:Stepper>Previous / page numbers / Next, with ellipses for skipped ranges. The current page is outlined; Previous and Next disable at the ends.
shellui-native add paginationProperties: Page (two-way, 1-based), TotalPages, SiblingCount (pages shown each side of
the current one, default 1), ShowLabels (text next to the chevrons) · Events: PageChanged
<ui:Pagination Page="{Binding Page}" TotalPages="20" PageChanged="OnPageChanged" />Pill label. Variants: Default, Secondary, Outline, Destructive, Success, Warning, Info
<ui:Badge Text="New" Variant="Success" />Ghost icon button that copies Text to the clipboard; the icon turns into a check for two
seconds. Label adds text next to the icon.
shellui-native add copy-buttonProperties: Text (what gets copied), Label · Events: Copied · Methods: CopyAsync()
<ui:CopyButton Text="shellui-native add button" />
<ui:CopyButton Text="{Binding ShareUrl}" Label="Copy link" />Keyboard key hint. Properties: Text
<ui:Kbd Text="Ctrl" /> <ui:Kbd Text="K" />Dashboard metric: a muted title with an optional icon, the value in large bold text, a change pill tinted by trend, and an optional description.
shellui-native add stat-cardProperties: Title, Value, Change, Trend (Neutral, Up, Down), Description, Icon
<ui:StatCard Title="Total revenue" Value="$45,231.89" Change="+20.1%" Trend="Up"
Icon="DollarSign" Description="from last month" />Vertical list of events joined by a hairline. Each item has a marker (a dot, or a circled icon), a title, a time and details.
shellui-native add timelineProperties (TimelineItem): Title, Time, Text, Icon, IsActive · a child view becomes
the item's body
<ui:Timeline>
<ui:TimelineItem Title="Order placed" Time="09:12" Text="We received your order." />
<ui:TimelineItem Title="Shipped" Time="14:40" Icon="Send" IsActive="True">
<ui:Badge Text="In transit" Variant="Info" HorizontalOptions="Start" />
</ui:TimelineItem>
</ui:Timeline>8px bar; the track is the fill color at 20%. Animates to new values.
Properties: Value, Maximum, Variant (Default, Success, Warning, Destructive), ShowLabel
Pulsing placeholder in the Muted token (opacity 1 → 0.5 → 1 every 2s, only while on screen).
Properties: CornerRadius (default 6) plus WidthRequest / HeightRequest
Rows inside a rounded border: a header row in the muted foreground, body rows divided by a hairline that tint on hover, and an optional caption.
shellui-native add tableProperties (Table): Columns (Grid column widths, e.g. 2*,*,*,100), Caption, Bordered
(true), MinimumContentWidth (below this width the table scrolls sideways instead of squeezing)
TableRow: IsSelected, Tapped event · TableHead: Text, HorizontalTextAlignment ·
TableCell: Text, HorizontalTextAlignment, IsBold, or any child view
<ui:Table Columns="*,*,1.4*,90" Caption="A list of your recent invoices.">
<ui:TableHeader>
<ui:TableHead Text="Invoice" />
<ui:TableHead Text="Status" />
<ui:TableHead Text="Method" />
<ui:TableHead Text="Amount" HorizontalTextAlignment="End" />
</ui:TableHeader>
<ui:TableRow Tapped="OnRowTapped">
<ui:TableCell Text="INV001" IsBold="True" />
<ui:TableCell><ui:Badge Text="Paid" Variant="Success" HorizontalOptions="Start" /></ui:TableCell>
<ui:TableCell Text="Credit Card" />
<ui:TableCell Text="$250.00" HorizontalTextAlignment="End" />
</ui:TableRow>
</ui:Table>Use star and fixed widths in Columns: every row is its own grid, so an Auto column would size
per row. For long lists, put a TableHeader above a CollectionView whose item template is a
TableRow with the same Columns set on the row itself.
One slide at a time in a rounded frame. Swipe, use the round arrow buttons or tap a dot to move. Each child view is a slide.
shellui-native add carouselProperties: Position (two-way), Loop (true), ShowArrows, ShowDots, AutoPlay,
AutoPlayInterval (ms, default 3000), AspectRatio (width / height, default 16:9; 0 to size it
with HeightRequest instead) · Events: PositionChanged · Methods: Next(), Previous()
<ui:Carousel AspectRatio="2" AutoPlay="True">
<Image Source="one.jpg" Aspect="AspectFill" />
<Image Source="two.jpg" Aspect="AspectFill" />
</ui:Carousel>Placeholder for an empty list or screen: icon in a muted tile, title, description and optional
actions, centered. Bordered="True" adds a dashed outline.
shellui-native add empty-stateProperties: Icon, Title, Description, Bordered · child views become the action row
<ui:EmptyState Icon="Folder" Title="No projects yet" Bordered="True"
Description="Create your first project to get started.">
<ui:Button Text="Create project" Icon="Plus" />
</ui:EmptyState>Circular image over a muted fallback — initials, or a user icon when Fallback is empty. The
fallback shows until the image loads and stays if it fails.
shellui-native add avatarProperties: Source (ImageSource), Fallback (initials), Size (Sm 32, Default 40, Lg 48, Xl 64)
<ui:Avatar Source="profile.png" Fallback="CN" />
<ui:Avatar Fallback="JD" Size="Lg" />Rotating loader icon; spins only while on screen. Properties: Size (Sm 16, Default 24, Lg 32), Token (color, default Foreground), IsRunning
<ui:Spinner />
<ui:Spinner Size="Lg" Token="MutedForeground" />Sonner-style notifications that stack in a corner, slide in, pause while hovered and dismiss
themselves (4s default). Call the static API from anywhere — toasts float above the current page.
A <ui:Toaster /> is optional: declare one anywhere on a page only to change position or count.
shellui-native add toast<ui:Toaster Position="BottomRight" MaxVisible="3" />Toast.Show("Event has been created", "Sunday, December 03 at 9:00 AM");
Toast.Success("Profile saved");
Toast.Error("Upload failed", "The file is larger than 10 MB.");
Toast.Warning("Storage almost full");
Toast.Info("New version available");
var id = Toast.Show("Message archived", actionText: "Undo", action: Undo);
Toast.Dismiss(id);Toaster: Position (BottomRight, BottomCenter, TopRight, TopCenter), MaxVisible (default 3)
Highlighted note on a faint tint of the variant color, with an icon, an optional title, text and/or any child content. Variants: Info, Warning, Danger, Tip, Default
shellui-native add calloutProperties: Variant, Title, Text, Icon (overrides the variant's icon) · child view becomes the body
<ui:Callout Variant="Tip" Title="Tip" Text="Press Escape to close the overlay on top." />
<ui:Callout Variant="Warning" Title="Heads up">
<Label Text="Any content can go here." />
</ui:Callout>Bordered callout with an icon. Properties: Title, Message, Variant (Default, Destructive, Success, Warning, Info)
<ui:Alert Title="Heads up!" Message="You can add components to your app using the CLI." />
<ui:Alert Title="Error" Message="Your session has expired." Variant="Destructive" />Modal overlays with a dimmed backdrop (tap it to close) and open/close animations: the dialog fades and zooms in, the drawer slides up with a grab handle, the sheet slides in from the side. Dialog and sheet have a close (X) button.
Declare them anywhere — next to the button that opens them is fine. When opened, the content is shown in a page-level layer above everything else (see How overlays float below).
shellui-native add dialog
shellui-native add drawer
shellui-native add sheetProperties: Open (also SetOpen(bool)) · Events: OpenChanged · Drawer/Sheet: Side (Left, Right, Top, Bottom)
<ui:Dialog x:Name="EditDialog">
<ui:DialogTrigger><ui:Button Text="Edit profile" Variant="Outline" /></ui:DialogTrigger>
<ui:DialogContent>
<ui:DialogHeader>
<ui:DialogTitle Text="Edit profile" />
<ui:DialogDescription Text="Make changes to your profile here." />
</ui:DialogHeader>
<ui:Input Text="Pedro Duarte" />
<ui:DialogFooter>
<ui:DialogClose><ui:Button Text="Cancel" Variant="Outline" /></ui:DialogClose>
<ui:Button Text="Save changes" Clicked="OnSave" />
</ui:DialogFooter>
</ui:DialogContent>
</ui:Dialog>DialogTrigger / DrawerTrigger / SheetTrigger and DialogClose wrap a Button or any view.
The trigger is optional: open from code with EditDialog.SetOpen(true).
A confirmation that requires a choice: no close button, and the backdrop doesn't dismiss it. Declare it anywhere, like Dialog.
shellui-native add alert-dialogProperties: Title, Description, ConfirmText ("Continue"), CancelText ("Cancel"; empty hides it), ConfirmVariant (ButtonVariant), optional extra content inside the tag
Events: Confirmed, Cancelled · Methods: Task<bool> ShowAsync(), SetOpen(bool)
<ui:AlertDialog x:Name="DeleteDialog"
Title="Are you absolutely sure?"
Description="This action cannot be undone."
ConfirmText="Delete account"
ConfirmVariant="Destructive" />if (await DeleteDialog.ShowAsync())
await DeleteAccountAsync();AlertDialogTrigger wraps a Button to open it from XAML instead.
Panels that float next to their trigger — below it, or above when there is no room, and always inside the window. Clicking outside, or opening another dropdown, popover or select, closes them.
shellui-native add dropdown
shellui-native add popoverProperties: IsOpen (also SetOpen, Toggle, Close) · Events: IsOpenChanged
DropdownItem: Text, Icon · Events: Clicked (the menu closes first)
<ui:Dropdown>
<ui:DropdownTrigger>
<ui:Button Text="Open menu" Variant="Outline" Icon="ChevronDown" IconPosition="Right" />
</ui:DropdownTrigger>
<ui:DropdownContent>
<ui:DropdownItem Text="Profile" Icon="User" Clicked="OnProfile" />
<ui:DropdownItem Text="Settings" Icon="Settings" Clicked="OnSettings" />
</ui:DropdownContent>
</ui:Dropdown>
<ui:Popover>
<ui:PopoverTrigger><ui:Button Text="Open popover" Variant="Outline" /></ui:PopoverTrigger>
<ui:PopoverContent>
<Label Text="Dimensions" />
<ui:Input Placeholder="Width" />
</ui:PopoverContent>
</ui:Popover>Right-click the trigger (long-press on touch) to open a menu at the pointer. Clicking outside, picking an item or pressing Escape closes it.
shellui-native add context-menuContextMenuItem: Text, Icon, Shortcut (hint text, display only), IsDestructive,
IsEnabled · Events: Clicked · ContextMenuSeparator divides groups ·
ContextMenu.OpenAt(point) opens it from code
<ui:ContextMenu>
<ui:ContextMenuTrigger>
<Border HeightRequest="120"> ... </Border>
</ui:ContextMenuTrigger>
<ui:ContextMenuContent>
<ui:ContextMenuItem Text="Copy" Icon="Copy" Shortcut="Ctrl+C" Clicked="OnCopy" />
<ui:ContextMenuSeparator />
<ui:ContextMenuItem Text="Delete" Icon="Trash2" IsDestructive="True" Clicked="OnDelete" />
</ui:ContextMenuContent>
</ui:ContextMenu>On touch devices the menu opens under the trigger rather than at the finger.
Small label shown above (or below) a view after the pointer rests on it. Pointer devices only.
shellui-native add tooltipProperties: Text, Placement (Top, Bottom), Delay (ms, default 400)
<ui:Tooltip Text="Add to library">
<ui:Button Icon="Plus" Size="Icon" Variant="Outline" />
</ui:Tooltip>Rich content that floats next to its trigger while the pointer is over the trigger or the card.
shellui-native add hover-cardProperties: OpenDelay (300 ms), CloseDelay (200 ms), IsOpen
On touch devices (no pointer hover) tapping the trigger toggles the card, and tapping outside closes it.
<ui:HoverCard>
<ui:HoverCardTrigger><ui:Button Text="@shellui" Variant="Link" /></ui:HoverCardTrigger>
<ui:HoverCardContent>
<Label Text="Beautifully designed components for .NET." />
</ui:HoverCardContent>
</ui:HoverCard>Open dialogs, drawers, sheets and popups (select, combobox, dropdown, popover, date and time
pickers) are tracked newest-last in ShellDismiss. Escape on Windows and the back button
on Android close the one on top; an Alert Dialog treats that as Cancel. With nothing open the key
keeps its normal behavior. Call ShellDismiss.DismissTop() to do the same from your own code.
ShellPortal (in Shell.cs) keeps one layer above each page's content. As the page appears, its
content is wrapped in a Grid, once, and the layer is that Grid's last child. Dialogs, drawers, sheets, menus, selects, tooltips, hover cards and toasts are all
placed in that layer, so they are never clipped by a ScrollView and always draw on top. Content
moved there keeps a link to its component, so bindings and lookups keep working.
The layer is edge-to-edge: backdrops dim the whole window, drawers and sheets run under the
system bars, and their content (plus popups and toasts) is kept clear of the bars and notch with
ShellPortal.GetSafeInsets. If you build your own overlay chrome, call
ShellPortal.EdgeToEdge(...) on its layouts so MAUI doesn't inset them a second time.
WinUI gives a window 250 layout passes to settle, and a very large page can run out and crash at
startup with Layout cycle detected. The demo hit this when every component was on one page:
WinUI's trace showed layout still reaching new elements when the passes ran out, with Border
outline updates each asking for another pass. An ordinary screen is nowhere near the limit; a
"kitchen sink" page is.
If you hit it, lay out less at once: split the page, or keep sections collapsed
(IsVisible="False") until they are needed, since hidden content is not laid out. The demo does
this, showing one category of components at a time.
shellui-native add button # single
shellui-native add button input card # several
shellui-native add button --force # overwrite an installed copyshellui-native list
shellui-native list --installed
shellui-native list --availableDependencies install automatically. Almost every component depends on shell (theme tokens and
core helpers); components that draw icons also depend on icon.
Parts of a compositional family (dialog-trigger, accordion-item, card-header, …) find their
parent at runtime, so they only build next to it. Adding a part on its own installs the whole
family: shellui-native add dialog-trigger installs dialog and all its parts.
| Component | Auto-installs |
|---|---|
| button | shell, icon, button-variants |
| card | shell, card-header, card-content, card-footer |
| select, combobox, time-picker, checkbox, alert, callout, breadcrumb-item, theme-toggle, avatar, spinner, toggle, pagination, empty-state | shell, icon |
| input-otp | shell |
| alert-dialog | shell, element-extensions, button |
| toast | shell, icon, button |
| dialog | shell, dialog-trigger, dialog-content, dialog-header, dialog-footer, dialog-title, dialog-description, dialog-close |
| drawer / sheet | shell, *-trigger, *-content (content also installs icon for the close button) |
| dropdown / popover | shell, *-trigger, *-content (+ dropdown-item) |
| table | shell |
| carousel | shell, icon |
| context-menu | shell, icon, element-extensions |
| stepper | shell, icon, button |
| toggle-group | shell, icon, element-extensions |
| number-input, stat-card, timeline | shell, icon |
| tag-input | shell, icon, wrap-layout |
| kbd | shell |
| wrap-layout | — |
| multi-select | shell, icon, wrap-layout |
| tree-view | shell, icon, element-extensions |
| copy-button, link-card | shell, icon |
| aspect-ratio | — |
| hover-card | shell, hover-card-trigger, hover-card-content |
| date-picker | shell, icon, calendar |
| tooltip | shell |
| collapsible / accordion / tabs | element-extensions + their sub-components |
| breadcrumb | breadcrumb-item |
Upgrading an existing project: components now require the new Shell.cs (theme tokens). If
your project was initialized earlier, refresh it once with shellui-native add shell --force.