Xamarin MAUI Migration Advisor

August 21, 2026 · View on GitHub

A single PowerShell script that scans a Xamarin.Forms solution and reports what stands between it and .NET MAUI. No install, no account, no upload. It reads local files and writes one self-contained HTML report for engineering, product, and budget approvers.

Privacy

  • No network calls, no uploads, no telemetry. The script is a single file and writes only the HTML report.
  • The report contains file paths and short code excerpts from your solution, so review it before sharing outside your team.

Why now

A Xamarin app can no longer ship updates to either store:

  • App Store: since 28 April 2026, uploads require Xcode 26 / iOS 26 SDK. Xamarin.iOS cannot produce them. (Apple)
  • Google Play: from 31 August 2026, apps must target Android 16 (API 36), with an extension to 1 November 2026. Xamarin.Android cannot reach it. (Google)

Usage

.\XamarinMAUIMigration.ps1                                        # scan current folder
.\XamarinMAUIMigration.ps1 -Path C:\src\MyApp                     # scan a specific path
.\XamarinMAUIMigration.ps1 -Path C:\src\MyApp -OutputPath C:\temp\report.html
.\XamarinMAUIMigration.ps1 -SkipReport                           # console summary only
.\XamarinMAUIMigration.ps1 -IncludeTestProjects                  # include test projects

Omit -Path to scan the current folder. The report is written to .\xamarin-maui-migration-readiness.html unless -OutputPath says otherwise.

ParameterDefaultDescription
-Path.Root folder to scan
-OutputPath.\xamarin-maui-migration-readiness.htmlHTML report path
-SkipReportoffConsole summary only
-IncludeTestProjectsoffInclude projects that look like test projects
-MaxFileSizeKB2048Skip source files larger than this

Runs on Windows, macOS, and Linux. Requires Windows PowerShell 5.1 or PowerShell 7+. No modules or dependencies.

Skipped folders: bin, obj, packages, node_modules, .git, .vs, TestResults, artifacts.

Skipped files: *.g.cs, *.designer.cs, *.generated.cs.

What it finds

AreaDetail
Custom renderersExportRenderer registrations and renderer classes, with file and line
Platform effectsPlatformEffect implementations and ExportEffect registrations
Projects not auto-convertedUWP, iOS extensions, iOS/Android binding projects, Xamarin.Mac, tvOS, watchOS
NuGet dependencies~60 package rules: absorbed, renamed, replaced, archived, blocked
Removed/obsoleted APIsDevice.*, Application.Properties, MessagingCenter, DependencyService
.NET 11 blockersMicrosoft.Maui.Controls.Compatibility usage
XAML issuesRelativeLayout, Frame, TableView, retired OnPlatform targets
Syncfusion mappingXamarin → MAUI package mapping where Syncfusion packages are present
Effort estimateDeveloper-days, with every rate shown so you can substitute your own

Every finding cites the file and line it came from.

Readiness score

A 1–100 number measuring blocker density, so a large app isn't penalised just for being large:

penalty = 10×critical + 4×high + 1×medium + 0.2×low
scale   = 40 + 8×projects + 0.4×sourceFiles
score   = 100 × e^(−penalty/scale)
ScoreBand
70–100Straightforward
45–69Moderate
25–44Substantial
1–24Major

The constants are judgment, not measurement. The report prints the full calculation with your numbers substituted in, so you can disagree and recompute. The same applies to the effort model: every rate is listed line by line.

Limitations

Static text analysis, not compilation. It cannot see:

  • Runtime behaviour, reflection, or dynamically constructed types
  • Source generators or code behind conditional compilation
  • Transitive NuGet dependencies (only packages.config and PackageReference are read)
  • Whether a renderer is trivial or weeks of work; it counts, it doesn't weigh

Also worth knowing:

  • The effort estimate is a planning input, not a quote. It assumes the app currently builds and excludes new features, redesign, QA cycles, store review, and team ramp-up.
  • An Unknown package is not a verdict. It means not in the rule set, so check it yourself.
  • Package guidance goes stale as community ports appear and are abandoned. Entries marked verify in the report warrant a second pair of eyes.

Found something wrong? Open an issue. That's the fastest way to improve this for the next team.

Contribute

Useful contributions: package, API, and XAML rule updates, plus corrections to migration guidance. Everything the tool knows lives in one marked section at the top of the script.

@{
   Id='Some.Xamarin.Package'
   Status='Replaced'                    # Builtin | Renamed | Replaced | Blocked | Check | Ok
   Target='Some.Maui.Package'
   Note='What changes, and what to watch for.'
   Confidence='Verify'                  # omit if certain
}
StatusMeaning
BuiltinAbsorbed into .NET MAUI or .NET; remove the package
RenamedSame library, new package id
ReplacedDifferent library, established successor
BlockedNo MAUI path; needs a decision, likely a rewrite
CheckSupports MAUI from some version; verify the version in use
OkWorks unchanged

Add API and XAML rules the same way in $script:ApiRules and $script:XamlRules.

About

Built by Syncfusion, an active contributor to the .NET MAUI open-source project, including migrating MAUI's own UI test suite from Xamarin.UITest to Appium, which is a large part of why this tool has opinions about what does and does not survive the move.

"We are thrilled to welcome Syncfusion as active contributors to the .NET MAUI open-source project. Their commitment to the success of .NET MAUI is incredible and their expertise in this space invaluable."

David Ortinau, Principal Product Manager, .NET MAUI, Microsoft

This tool is useful whether or not you use Syncfusion controls. It reports on your whole solution, not on ours.

License

MIT. See LICENSE.