
Uno Platform
- 16 installs
- 466 repo stars
- Updated July 25, 2026
- managedcode/dotnet-skills
Helps with ai & agent building tasks.
About
uno-platform is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- uno-platform
- AI & Agent Building
- AI-coding skill
Uno Platform by the numbers
- 16 all-time installs (skills.sh)
- +1 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #11,040 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/managedcode/dotnet-skills --skill uno-platformAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 16 |
|---|---|
| repo stars | ★ 466 |
| Last updated | July 25, 2026 |
| Repository | managedcode/dotnet-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Uno Platform
Trigger On
- building cross-platform apps from a single C# and XAML codebase
- targeting WebAssembly, iOS, Android, macOS, Linux, and Windows simultaneously
- migrating WPF or UWP applications to cross-platform
- implementing pixel-perfect UI across all platforms
- using WinUI/UWP APIs on non-Windows platforms
Documentation
References
See detailed examples in the references/ folder:
- `patterns.md` — MVUX, XAML, navigation, and performance patterns
Platform Support
| Platform | Rendering | Notes |
|---|---|---|
| Windows | WinUI 3 | Native Windows App SDK |
| WebAssembly | Skia/Canvas | Runs in browser |
| iOS | Skia/Metal | Native iOS app |
| Android | Skia/OpenGL | Native Android app |
| macOS | Skia/Metal | Mac Catalyst or AppKit |
| Linux | Skia/X11 | GTK or Framebuffer |
Workflow
1. Choose the right template — Uno Platform offers various templates for different scenarios 2. Understand rendering modes — Skia vs native rendering affects performance and fidelity 3. Apply MVVM or MVUX patterns — keep views dumb, logic in ViewModels 4. Handle platform differences — use conditional XAML or partial classes 5. Test on all target platforms — behavior varies across platforms
Project Structure
MyApp/
├── MyApp/ # Shared code
│ ├── App.xaml # Application entry
│ ├── MainPage.xaml # Main page
│ ├── Presentation/ # ViewModels (MVUX/MVVM)
│ ├── Business/ # Business logic
│ └── Services/ # Platform services
├── MyApp.Wasm/ # WebAssembly head
├── MyApp.Mobile/ # iOS and Android head
├── MyApp.Skia.Gtk/ # Linux head
├── MyApp.Skia.WPF/ # Windows Skia head
└── MyApp.Windows/ # Native WinUI headMVUX Pattern (Uno Extensions)
Model Definition
public partial record MainModel
{
public IListFeed<TodoItem> Items => ListFeed.Async(LoadItems);
private async ValueTask<IImmutableList<TodoItem>> LoadItems(CancellationToken ct)
{
var items = await _todoService.GetAllAsync(ct);
return items.ToImmutableList();
}
}View Binding with FeedView
<Page xmlns:uen="using:Uno.Extensions.Navigation.UI">
<utu:FeedView Source="{Binding Items}">
<utu:FeedView.ValueTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:TodoItem">
<TextBlock Text="{Binding Title}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</utu:FeedView.ValueTemplate>
<utu:FeedView.ProgressTemplate>
<DataTemplate>
<ProgressRing IsActive="True" />
</DataTemplate>
</utu:FeedView.ProgressTemplate>
</utu:FeedView>
</Page>Classic MVVM with MVVM Toolkit
ViewModel
public partial class MainViewModel(ITodoService todoService) : ObservableObject
{
[ObservableProperty]
private ObservableCollection<TodoItem> _items = [];
[ObservableProperty]
private bool _isLoading;
[RelayCommand]
private async Task LoadItemsAsync()
{
IsLoading = true;
try
{
var items = await todoService.GetAllAsync();
Items = new ObservableCollection<TodoItem>(items);
}
finally
{
IsLoading = false;
}
}
}Platform-Specific Code
Conditional XAML
<TextBlock Text="Welcome"
xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:android="http://uno.ui/android"
xmlns:ios="http://uno.ui/ios"
xmlns:wasm="http://uno.ui/wasm">
<win:TextBlock.Foreground>
<SolidColorBrush Color="Blue" />
</win:TextBlock.Foreground>
<android:TextBlock.Foreground>
<SolidColorBrush Color="Green" />
</android:TextBlock.Foreground>
</TextBlock>Partial Classes
// Services/DeviceService.cs (shared)
public partial class DeviceService
{
public partial string GetDeviceInfo();
}
// Services/DeviceService.wasm.cs
public partial class DeviceService
{
public partial string GetDeviceInfo() => "WebAssembly";
}
// Services/DeviceService.Android.cs
public partial class DeviceService
{
public partial string GetDeviceInfo() =>
$"Android {Android.OS.Build.VERSION.Release}";
}Hot Reload and Development
// Enable Hot Reload in App.xaml.cs
public App()
{
this.InitializeComponent();
#if DEBUG
// Enable Hot Reload
this.EnableHotReload();
#endif
}Anti-Patterns to Avoid
| Anti-Pattern | Why It's Bad | Better Approach |
|---|---|---|
| Platform code in shared | Breaks compilation | Use partial classes or #if |
| Ignoring Skia differences | Visual bugs | Test on all renderers |
| WPF assumptions | Not all APIs exist | Check Uno API coverage |
| Heavy XAML | Slow on WASM | Virtualize, simplify |
| Synchronous loading | UI freezes | Always use async |
Performance Best Practices
1. Use virtualized lists:
<ListView ItemsSource="{Binding Items}"
VirtualizingPanel.VirtualizationMode="Recycling" />2. Lazy load resources:
// Load images on demand
var image = await ImageSource.LoadFromUriAsync(uri);3. Minimize XAML complexity:
- Avoid deep nesting
- Use compiled bindings (
x:Bindwhere supported) - Consider Skia-specific optimizations
4. WebAssembly specific:
- Minimize interop calls
- Use
InvokeAsyncfor JS interop - Consider AOT compilation for performance
Uno Extensions
// Use Uno.Extensions for enhanced patterns
var builder = this.CreateBuilder(args)
.Configure(host => host
.UseConfiguration()
.UseLocalization()
.UseNavigation()
.UseMvux()
.ConfigureServices(services =>
{
services.AddSingleton<ITodoService, TodoService>();
}));Deliver
- single codebase running on web, mobile, and desktop
- consistent UI/UX across all platforms
- platform-specific optimizations where needed
- MVVM or MVUX patterns for testability
Validate
- app builds and runs on all target platforms
- platform-specific features work correctly
- performance is acceptable on WebAssembly
- Hot Reload works during development
- no WPF/UWP-only APIs are used without fallbacks
{
"version": "1.0.0",
"category": "Cross-Platform UI",
"package_prefix": "Uno.WinUI"
}
Uno Platform Patterns Reference
MVUX Patterns
Basic Model with Feed
public partial record ProductsModel(IProductService Products)
{
public IListFeed<Product> Items => ListFeed.Async(Products.GetAllAsync);
}Model with State
public partial record ProductDetailModel(IProductService Products)
{
public IState<int> ProductId => State<int>.Empty(this);
public IFeed<Product> Product => ProductId
.SelectAsync(async (id, ct) => await Products.GetAsync(id, ct));
}Model with Commands
public partial record OrderModel(IOrderService Orders)
{
public IState<string> CustomerName => State<string>.Value(this, () => string.Empty);
public IState<string> ProductId => State<string>.Value(this, () => string.Empty);
public IState<int> Quantity => State<int>.Value(this, () => 1);
public async ValueTask SubmitOrder(CancellationToken ct)
{
var order = new Order
{
CustomerName = await CustomerName,
ProductId = await ProductId,
Quantity = await Quantity
};
await Orders.CreateAsync(order, ct);
}
}Pagination Pattern
public partial record PaginatedModel(IProductService Products)
{
public IState<int> CurrentPage => State<int>.Value(this, () => 0);
public IState<int> PageSize => State<int>.Value(this, () => 20);
public IListFeed<Product> Items =>
CurrentPage.CombineWith(PageSize)
.SelectAsync(async (state, ct) =>
{
var (page, size) = state;
return await Products.GetPageAsync(page, size, ct);
})
.AsListFeed();
public async ValueTask NextPage()
{
await CurrentPage.Update(p => p + 1);
}
public async ValueTask PreviousPage()
{
await CurrentPage.Update(p => Math.Max(0, p - 1));
}
}XAML Patterns
Responsive Layout
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="Narrow">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="0" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MainContent.Orientation" Value="Vertical" />
<Setter Target="SidePanel.Visibility" Value="Collapsed" />
</VisualState.Setters>
</VisualState>
<VisualState x:Name="Wide">
<VisualState.StateTriggers>
<AdaptiveTrigger MinWindowWidth="800" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="MainContent.Orientation" Value="Horizontal" />
<Setter Target="SidePanel.Visibility" Value="Visible" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<StackPanel x:Name="MainContent">
<Grid x:Name="SidePanel" Width="300" />
<Grid x:Name="ContentPanel" />
</StackPanel>
</Grid>Platform-Specific Styling
<Style TargetType="Button" x:Key="PlatformButton">
<Setter Property="Padding" Value="16,8" />
<!-- Windows-specific -->
<win:Setter Property="CornerRadius" Value="4" />
<!-- Android-specific -->
<android:Setter Property="Background" Value="{StaticResource MaterialPrimary}" />
<!-- iOS-specific -->
<ios:Setter Property="Background" Value="{StaticResource iOSBlue}" />
</Style>FeedView with All States
<utu:FeedView Source="{Binding Products}">
<!-- Loading state -->
<utu:FeedView.ProgressTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<ProgressRing IsActive="True" Width="40" Height="40" />
<TextBlock Text="Loading..." Margin="0,8,0,0" />
</StackPanel>
</DataTemplate>
</utu:FeedView.ProgressTemplate>
<!-- Error state -->
<utu:FeedView.ErrorTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<SymbolIcon Symbol="Warning" />
<TextBlock Text="{Binding Message}" Margin="0,8,0,0" />
<Button Content="Retry" Command="{Binding RetryCommand}" />
</StackPanel>
</DataTemplate>
</utu:FeedView.ErrorTemplate>
<!-- Empty state -->
<utu:FeedView.NoneTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<SymbolIcon Symbol="List" />
<TextBlock Text="No items found" Margin="0,8,0,0" />
</StackPanel>
</DataTemplate>
</utu:FeedView.NoneTemplate>
<!-- Success state -->
<utu:FeedView.ValueTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Product">
<Grid Padding="12">
<TextBlock Text="{x:Bind Name}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</utu:FeedView.ValueTemplate>
</utu:FeedView>Platform Service Patterns
Abstracted Platform Service
// Shared interface
public interface IPlatformService
{
string GetPlatformName();
Task<bool> RequestPermissionAsync(string permission);
Task ShareAsync(string text, string? title = null);
}
// Shared implementation with partial methods
public partial class PlatformService : IPlatformService
{
public partial string GetPlatformName();
public partial Task<bool> RequestPermissionAsync(string permission);
public partial Task ShareAsync(string text, string? title = null);
}// Platforms/Android/PlatformService.cs
public partial class PlatformService
{
public partial string GetPlatformName() => "Android";
public async partial Task<bool> RequestPermissionAsync(string permission)
{
var status = await Permissions.RequestAsync<Permissions.StorageRead>();
return status == PermissionStatus.Granted;
}
public async partial Task ShareAsync(string text, string? title)
{
var intent = new Intent(Intent.ActionSend);
intent.SetType("text/plain");
intent.PutExtra(Intent.ExtraText, text);
Platform.CurrentActivity.StartActivity(Intent.CreateChooser(intent, title));
}
}// Platforms/iOS/PlatformService.cs
public partial class PlatformService
{
public partial string GetPlatformName() => "iOS";
public async partial Task<bool> RequestPermissionAsync(string permission)
{
// iOS permission handling
}
public async partial Task ShareAsync(string text, string? title)
{
var controller = new UIActivityViewController(
new NSObject[] { new NSString(text) }, null);
await UIApplication.SharedApplication.KeyWindow
.RootViewController.PresentViewControllerAsync(controller, true);
}
}Navigation Patterns
Region-Based Navigation
// App.xaml.cs
public App()
{
this.InitializeComponent();
var builder = this.CreateBuilder(args)
.Configure(host => host
.UseNavigation(RegisterRoutes));
}
void RegisterRoutes(IViewRegistry views, IRouteRegistry routes)
{
views.Register(
new ViewMap<MainPage, MainModel>(),
new ViewMap<ProductPage, ProductModel>(),
new ViewMap<SettingsPage, SettingsModel>()
);
routes.Register(
new RouteMap("", View: views.FindByViewModel<MainModel>()),
new RouteMap("Product", View: views.FindByViewModel<ProductModel>()),
new RouteMap("Settings", View: views.FindByViewModel<SettingsModel>())
);
}Navigation with Parameters
public partial record ProductListModel(INavigator Navigator)
{
public async ValueTask NavigateToProduct(Product product)
{
await Navigator.NavigateViewModelAsync<ProductModel>(
this, data: new { ProductId = product.Id });
}
}
public partial record ProductModel(INavigator Navigator)
{
// Receives ProductId from navigation data
public IState<int> ProductId => State<int>.Value(this, () => 0);
}Performance Patterns
Virtualized List
<ListView ItemsSource="{Binding Items}"
VirtualizingStackPanel.VirtualizationMode="Recycling"
VirtualizingStackPanel.IsVirtualizing="True">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>Deferred Loading
<Grid>
<!-- Load expensive content only when visible -->
<Grid x:Name="ExpensiveContent"
x:Load="{x:Bind ViewModel.ShowDetails, Mode=OneWay}">
<local:ExpensiveUserControl />
</Grid>
</Grid>Image Optimization
// Use appropriate image sizes per platform
public static ImageSource GetOptimizedImage(string basePath)
{
#if __ANDROID__
var density = Android.Content.Res.Resources.System.DisplayMetrics.Density;
var suffix = density switch
{
< 1.5f => "mdpi",
< 2.0f => "hdpi",
< 3.0f => "xhdpi",
_ => "xxhdpi"
};
return ImageSource.FromFile($"{basePath}_{suffix}.png");
#elif __IOS__
return ImageSource.FromFile($"{basePath}@2x.png");
#else
return ImageSource.FromFile($"{basePath}.png");
#endif
}Theme Patterns
Light/Dark Theme Support
public class ThemeService : IThemeService
{
public void SetTheme(AppTheme theme)
{
var resources = Application.Current.Resources;
var mergedDictionaries = resources.MergedDictionaries;
mergedDictionaries.Clear();
mergedDictionaries.Add(theme switch
{
AppTheme.Light => new LightTheme(),
AppTheme.Dark => new DarkTheme(),
_ => new SystemTheme()
});
}
}<!-- LightTheme.xaml -->
<ResourceDictionary>
<Color x:Key="BackgroundColor">#FFFFFF</Color>
<Color x:Key="TextColor">#000000</Color>
<Color x:Key="AccentColor">#0078D4</Color>
</ResourceDictionary>
<!-- DarkTheme.xaml -->
<ResourceDictionary>
<Color x:Key="BackgroundColor">#1E1E1E</Color>
<Color x:Key="TextColor">#FFFFFF</Color>
<Color x:Key="AccentColor">#0078D4</Color>
</ResourceDictionary>