
Xamarin Forms Migration
- 28 installs
- 163 repo stars
- Updated July 6, 2026
- davidortinau/maui-skills
Migrates Xamarin.Forms apps to .NET MAUI, covering project conversion, namespace renames, layout changes, and renderer-to-handler migration.
About
A workflow guide for migrating Xamarin.Forms apps to .NET MAUI, covering SDK-style project conversion, namespace renames, layout behavior changes, renderer-to-handler migration and effects-to-behaviors redesign. A developer uses it when porting a Xamarin.Forms app to MAUI.
- SDK-style project conversion and namespace renames
- Renderer-to-handler migration and effects-to-behaviors redesign
Xamarin Forms Migration by the numbers
- 28 all-time installs (skills.sh)
- Ranked #684 of 1,039 Mobile Development skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/davidortinau/maui-skills --skill xamarin-forms-migrationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 28 |
|---|---|
| repo stars | ★ 163 |
| Last updated | July 6, 2026 |
| Repository | davidortinau/maui-skills ↗ |
What it does
Migrates Xamarin.Forms apps to .NET MAUI, covering project conversion, namespace renames, layout changes, and renderer-to-handler migration.
Files
Xamarin.Forms → .NET MAUI Migration
For project templates, namespace tables, API deprecation tables, and reference code, see references/forms-migration-api.md.
⚠️ Do not use the .NET Upgrade Assistant. Apply namespace renames, project file updates, and package replacements directly. Build after each batch of changes and use compiler errors to guide the next round of fixes.
Migration Workflow
1. Create new .NET MAUI project (single-project — multi-project causes AOT/build errors) 2. Copy cross-platform code + platform code into Platforms/<platform>/ 3. Update namespaces (XAML + C# + Essentials — see references/forms-migration-api.md) 4. Fix layout behavior changes (see below) 5. Migrate renderers → handlers (not shimmed renderers) 6. Migrate effects → behaviors 7. Remove Microsoft.Maui.Controls.Compatibility package 8. Update NuGet dependencies 9. Migrate app data stores 10. Delete bin//obj//Resource.designer.cs, build, test iteratively 11. Verify against .NET 10 deprecated API list
Strategy: Create a new project and copy code into it — don't edit the existing project file in place.
Critical Layout Pitfalls
⚠️ Default Spacing Changed to Zero
MAUI zeroes out spacing that Xamarin.Forms set to 6. Your layouts will break silently:
<!-- ❌ Looks fine in Xamarin.Forms, cramped in MAUI -->
<Grid>...</Grid>
<StackLayout>...</StackLayout>
<!-- ✅ Add explicit values or restore via implicit styles in App.xaml -->
<Style TargetType="Grid">
<Setter Property="ColumnSpacing" Value="6"/>
<Setter Property="RowSpacing" Value="6"/>
</Style>Field advice: Specify all layout values explicitly — don't rely on platform defaults.
⚠️ *AndExpand Is Obsolete — No Silent Warning
<!-- ❌ Silently ignored in MAUI — image won't expand -->
<StackLayout>
<Label Text="Hello world!"/>
<Image VerticalOptions="FillAndExpand" Source="dotnetbot.png"/>
</StackLayout>
<!-- ✅ Convert to Grid with star sizing -->
<Grid RowDefinitions="Auto, *">
<Label Text="Hello world!"/>
<Image Grid.Row="1" Source="dotnetbot.png"/>
</Grid>Field advice: Flatten layout trees. Replace nested hierarchies with single Grid layouts.
⚠️ ScrollView in StackLayout Doesn't Scroll
ScrollView inside StackLayout expands to full content height (no scroll). Place ScrollView in a Grid with a constrained row instead.
Other Layout Traps
| Issue | Fix |
|---|---|
| Grid columns/rows not declared → layout broken | Always add explicit ColumnDefinitions/RowDefinitions |
Frame measures differently | Migrate to Border with StrokeShape |
BoxView invisible (default 0×0 in MAUI) | Set explicit WidthRequest/HeightRequest |
RelativeLayout | Replace with Grid (compatibility namespace only) |
Renderer → Handler Migration
⚠️ Migrate all renderers to handlers. Do NOT use shimmed renderers — they create parent wrapper views that hurt performance.
Preferred: Customize existing handlers with mapper methods:
// ✅ Extend existing handler — no new class needed
Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("NoBorder", (handler, view) =>
{
#if ANDROID
handler.PlatformView.Background = null;
#elif IOS || MACCATALYST
handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
#endif
});For completely new native views, create a full handler (see maui-custom-handlers skill).
Effects → Behaviors
⚠️ Effects are now Behaviors — this requires redesign, not just renaming.
For new development, prefer behaviors or handler mapper customizations over effects.
⚠️ Do NOT Use the Compatibility Package
Microsoft.Maui.Controls.Compatibility causes cascading incompatibilities. Remove it and rebuild layouts natively.
Retired Dependencies
- App Center is retired → Replace with Sentry, Azure Monitor, or similar
- Visual Studio for Mac is retired → Use VS Code or Rider
Android-Specific Warnings
- Android migration is significantly harder than iOS — expect more UI bugs
- OEM-specific rendering differences not reproducible on emulators — test on physical devices
- Shadow rendering varies across OEMs/API levels — implement in platform-specific handler code
- Handler-level property changes don't auto-update on theme switch — manually handle theme changes
.NET 10 API Currency Warning
Migration is the perfect time to skip deprecated APIs entirely. Don't migrate Xamarin.Forms code to a MAUI API that's already on its way out. See the full deprecated API table in references/forms-migration-api.md and run the maui-current-apis skill.
Common Troubleshooting
| Issue | Fix |
|---|---|
Xamarin.* namespace doesn't exist | Update to Microsoft.Maui.* equivalent |
| CollectionView doesn't scroll | Place in Grid (not StackLayout) to constrain size |
| Pop-up under page on iOS | Use DisplayAlert from the ContentPage |
| Missing padding/margin/spacing | Add explicit values or implicit styles |
| Custom renderer broken | Migrate to handler |
| SkiaSharp broken | Update to SkiaSharp.Views.Maui package |
| Can't access App.Properties data | Migrate to Preferences |
Quick Checklist
1. ☐ Created new .NET MAUI project (single-project) 2. ☐ Copied cross-platform and platform code 3. ☐ Updated XAML namespace to http://schemas.microsoft.com/dotnet/2021/maui 4. ☐ Replaced Xamarin.Forms.* → Microsoft.Maui.* namespaces 5. ☐ Replaced Xamarin.Essentials → split MAUI namespaces 6. ☐ Added explicit Grid ColumnDefinitions/RowDefinitions 7. ☐ Replaced *AndExpand with Grid layouts 8. ☐ Added explicit spacing/padding values (MAUI defaults to 0) 9. ☐ Migrated renderers to handlers (not shimmed renderers) 10. ☐ Migrated effects to behaviors or MAUI effects 11. ☐ Removed Microsoft.Maui.Controls.Compatibility package 12. ☐ Updated NuGet dependencies for .NET compatibility 13. ☐ Migrated App.Properties, SecureStorage, VersionTracking data 14. ☐ Deleted bin/, obj/, and Resource.designer.cs 15. ☐ Tested on physical Android device 16. ☐ Profiled performance 17. ☐ Verified no .NET 10 deprecated APIs (run maui-current-apis skill)
Xamarin.Forms Migration API Reference
SDK-Style Project File Template
<!-- .NET MAUI single-project csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0-android;net8.0-ios;net8.0-maccatalyst</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">
$(TargetFrameworks);net8.0-windows10.0.19041.0
</TargetFrameworks>
<OutputType>Exe</OutputType>
<UseMaui>true</UseMaui>
<SingleProject>true</SingleProject>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>Replacenet8.0withnet9.0ornet10.0as appropriate for your target version.
---
XAML Namespace Changes
| Xamarin.Forms | .NET MAUI |
|---|---|
xmlns="http://xamarin.com/schemas/2014/forms" | xmlns="http://schemas.microsoft.com/dotnet/2021/maui" |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | (unchanged) |
---
C# Namespace Changes
| Xamarin.Forms Namespace | .NET MAUI Namespace |
|---|---|
Xamarin.Forms | Microsoft.Maui.Controls |
Xamarin.Forms.Xaml | Microsoft.Maui.Controls.Xaml |
Xamarin.Forms.PlatformConfiguration | Microsoft.Maui.Controls.PlatformConfiguration |
Xamarin.Forms.PlatformConfiguration.iOSSpecific | Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific |
Xamarin.Forms.PlatformConfiguration.AndroidSpecific | Microsoft.Maui.Controls.PlatformConfiguration.AndroidSpecific |
Xamarin.Forms.Shapes | Microsoft.Maui.Controls.Shapes |
Xamarin.Forms.StyleSheets | (removed — use MAUI styles) |
---
Xamarin.Essentials → .NET MAUI Namespaces
In .NET MAUI, Xamarin.Essentials functionality is built in. Remove the Xamarin.Essentials NuGet package and update using directives:
| Xamarin.Essentials | .NET MAUI Namespace |
|---|---|
Xamarin.Essentials (general) | Split across multiple namespaces below |
| App actions, permissions, version tracking | Microsoft.Maui.ApplicationModel |
| Contacts, email, networking | Microsoft.Maui.ApplicationModel.Communication |
| Battery, sensors, flashlight, haptics | Microsoft.Maui.Devices |
| Media picking, text-to-speech | Microsoft.Maui.Media |
| Clipboard, file sharing | Microsoft.Maui.ApplicationModel.DataTransfer |
| File picking, secure storage, preferences | Microsoft.Maui.Storage |
---
Default Spacing Value Changes
| Property | Xamarin.Forms Default | .NET MAUI Default |
|---|---|---|
Grid.ColumnSpacing | 6 | 0 |
Grid.RowSpacing | 6 | 0 |
StackLayout.Spacing | 6 | 0 |
<!-- Implicit styles to restore Xamarin.Forms defaults (add to App.xaml) -->
<Style TargetType="Grid">
<Setter Property="ColumnSpacing" Value="6"/>
<Setter Property="RowSpacing" Value="6"/>
</Style>
<Style TargetType="StackLayout">
<Setter Property="Spacing" Value="6"/>
</Style>---
Layout Behavior Differences
| Issue | Xamarin.Forms | .NET MAUI | Fix |
|---|---|---|---|
| Grid columns/rows | Inferred from XAML | Must be explicitly declared | Add ColumnDefinitions and RowDefinitions |
*AndExpand options | Supported on StackLayout | Obsolete — no effect on HorizontalStackLayout/VerticalStackLayout | Convert to Grid with * row/column sizes |
| StackLayout fill | Children could fill stacking direction | Children stack beyond available space | Use Grid when children need to fill space |
RelativeLayout | Built-in | Compatibility namespace only | Replace with Grid |
Frame | Built-in | Replaced by Border (Frame still works but measures differently) | Migrate to Border |
ScrollView in StackLayout | Compressed to fit | Expands to full content height (no scroll) | Place ScrollView in Grid with constrained row |
BoxView default size | 40×40 | 0×0 | Set explicit WidthRequest/HeightRequest |
Converting *AndExpand to Grid
<!-- BEFORE (Xamarin.Forms) -->
<StackLayout>
<Label Text="Hello world!"/>
<Image VerticalOptions="FillAndExpand" Source="dotnetbot.png"/>
</StackLayout>
<!-- AFTER (.NET MAUI) -->
<Grid RowDefinitions="Auto, *">
<Label Text="Hello world!"/>
<Image Grid.Row="1" Source="dotnetbot.png"/>
</Grid>---
Renderer to Handler Migration
Mapper Methods
| Mapper Method | When it runs |
|---|---|
PrependToMapping | Before default mapper |
ModifyMapping | Replaces default mapper |
AppendToMapping | After default mapper |
Customize Existing Handlers (Preferred)
// In MauiProgram.cs
Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping("NoBorder", (handler, view) =>
{
#if ANDROID
handler.PlatformView.Background = null;
#elif IOS || MACCATALYST
handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
#endif
});Shimmed Renderers (Fallback Only)
.NET MAUI provides shims for renderers deriving from FrameRenderer, ListViewRenderer, ShellRenderer, TableViewRenderer, and VisualElementRenderer:
1. Move code to Platforms/<platform>/ folders 2. Change Xamarin.Forms.* namespaces to Microsoft.Maui.* 3. Remove [assembly: ExportRenderer(...)] attributes 4. Register with ConfigureMauiHandlers / AddHandler
---
Effects Migration
// MauiProgram.cs
builder.ConfigureEffects(effects =>
{
effects.Add<FocusRoutingEffect, FocusPlatformEffect>();
});Effects migration steps: 1. Remove ResolutionGroupNameAttribute and ExportEffectAttribute 2. Remove Xamarin.Forms and Xamarin.Forms.Platform.* using directives 3. Combine RoutingEffect + PlatformEffect implementations into a single file with conditional compilation 4. Register with ConfigureEffects in MauiProgram.cs
---
NuGet Dependency Compatibility
| Compatible Frameworks | Incompatible Frameworks |
|---|---|
net8.0-android, monoandroid, monoandroidXX.X | |
net8.0-ios | monotouch, xamarinios, xamarinios10 |
net8.0-macos | monomac, xamarinmac, xamarinmac20 |
net8.0-tvos | xamarintvos |
---
App Data Migration
| Data Store | Migration Guide |
|---|---|
Application.Properties | Migrate to Preferences (Microsoft.Maui.Storage) |
| Secure Storage | Migrate from Xamarin.Essentials.SecureStorage to Microsoft.Maui.Storage.SecureStorage |
| Version Tracking | Migrate from Xamarin.Essentials.VersionTracking to Microsoft.Maui.ApplicationModel.VersionTracking |
| SkiaSharp | Update to SkiaSharp 2.88+ with SkiaSharp.Views.Maui |
---
MauiProgram.cs Entry Point
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
return builder.Build();
}
}---
.NET 10 Deprecated API Table
| Avoid in .NET 10 | Use Instead |
|---|---|
ListView, TableView | CollectionView |
Frame | Border with StrokeShape |
Device.RuntimePlatform | DeviceInfo.Platform |
Device.BeginInvokeOnMainThread() | MainThread.BeginInvokeOnMainThread() |
Device.OpenUri() | Launcher.OpenAsync() |
DependencyService | Constructor injection via builder.Services |
MessagingCenter | WeakReferenceMessenger (CommunityToolkit.Mvvm) |
DisplayAlert() / DisplayActionSheet() | DisplayAlertAsync() / DisplayActionSheetAsync() |
FadeTo(), RotateTo(), etc. | FadeToAsync(), RotateToAsync(), etc. |
Color.FromHex() | Color.FromArgb() |
Page.IsBusy | ActivityIndicator |