If you've ever hit a bug on Android that you couldn't reproduce on Windows, or struggled to get dotnet-trace working on an iOS device, you've felt the pain of .NET's runtime split. Since .NET 5 unified the platform under a single SDK, there's been an asterisk: mobile ran on Mono while everything else ran on CoreCLR. Different JIT behaviour, different GC characteristics, different diagnostics tooling, different bug surfaces.
Starting with .NET 11 Preview 4, that asterisk is gone. CoreCLR is now the default runtime for .NET MAUI on Android, iOS, and Mac Catalyst. Mono is still available as a fallback, but the direction is clear — .NET is converging on a single runtime for every platform.
This isn't a cosmetic change. It reshapes how mobile apps start, how you profile them, what compilation strategies are available, and what the future of .NET mobile development looks like.
Why Mono was there in the first place
When Xamarin brought C# to mobile over a decade ago, CoreCLR didn't run on ARM-based mobile operating systems. Mono was the only option — a portable, embeddable runtime that could target Android's Linux kernel and Apple's Darwin-based platforms. When .NET 5 arrived and unified the SDK, Mono stayed on mobile because CoreCLR simply wasn't ready for those targets.
That meant .NET developers lived with two runtimes. Your ASP.NET Core API ran on CoreCLR with tiered JIT compilation, Profile-Guided Optimisation (PGO), and a region-based GC. Your MAUI app on the same solution ran on Mono with its own JIT, its own GC, and its own set of quirks. A System.Text.Json serialisation bug might manifest on one runtime but not the other. A performance characteristic you relied on in server code might not hold on mobile.
The runtime team has been working toward this unification for years. CoreCLR gained Android support experimentally in .NET 10, and .NET 11 Preview 4 flips the default.
What changes when you target .NET 11
When you create or retarget a MAUI project to net11.0, your app automatically builds and runs on CoreCLR for Android, iOS, and Mac Catalyst. No MSBuild properties to set, no feature flags to enable. It just happens.
Here's what you get:
Tiered JIT compilation — CoreCLR's tiered compilation starts methods at a low-overhead tier and promotes hot paths to fully optimised native code. Mono's JIT was a single-pass compiler without this adaptive behaviour. The result is better steady-state throughput, particularly for CPU-bound work.
ReadyToRun (R2R) precompilation — Your assemblies ship with pre-compiled native code alongside the IL. At startup, the runtime uses the native code directly instead of JIT-compiling from scratch. R2R is enabled by default for CoreCLR builds, and on iOS and Mac Catalyst, composite R2R is always on for both debug and release configurations.
Profile-Guided Optimisation — PGO feeds runtime profiling data back into the JIT to make better inlining, devirtualisation, and layout decisions. This is the same PGO that has driven significant throughput improvements in ASP.NET Core benchmarks since .NET 7. Now it's available on your phone.
Region-based GC — CoreCLR's garbage collector uses a region-based memory model that reduces pause times and handles allocation-heavy workloads more efficiently than Mono's GC. For apps with complex UI rendering or frequent object allocation during scrolling and animation, this can make a noticeable difference.
Startup performance: the headline numbers
Microsoft's benchmarks show a baseline dotnet new maui app on a Pixel 7a launching in 0.4 seconds with CoreCLR and NativeAOT, compared to 1.1 seconds with Mono JIT — roughly a 2.75x improvement.
Even without NativeAOT, CoreCLR with R2R and PGO shows measurable startup improvements for simple apps. The combination of precompiled native code and adaptive JIT means less work at launch time.
// WARNING
Community reports indicate that larger, more complex Android apps may see regressions compared to Mono. Apps with heavy use of reflection, System.Linq.Expressions, or deep dependency injection graphs should benchmark carefully against their .NET 10 baselines.
The performance story isn't universally positive yet. Known issues include Type.GetType() performance regressions on Android and slower MethodInfo.Invoke() paths that affect apps relying heavily on data-binding and DI container resolution. Microsoft is actively tracking these and has asked developers to file issues with reproducible samples.
Full diagnostics on mobile
This is arguably the most practical day-to-day improvement. With CoreCLR as the runtime, the same diagnostic tools you use on server and desktop now work on mobile:
dotnet-tracecaptures detailed performance traces from Android devices and iOS simulatorsdotnet-countersstreams live runtime metrics — GC collections, thread pool usage, exception ratesdotnet-gcdumpcaptures GC heap snapshots for memory analysis
With Mono, mobile diagnostics required separate tooling, platform-specific workarounds, or simply weren't available. CoreCLR's diagnostic component is built into the runtime — you don't need to set EnableDiagnostics or configure anything special. Connect to the diagnostic port the same way you would for a server process.
// Collecting a trace from an Android device is now the same as any other .NET process
// dotnet-trace collect --process-id <pid> --providers Microsoft-DotNET-Runtime
For teams that maintain both server and mobile codebases, this unification means a single set of performance investigation skills and tooling across your entire stack.
The NativeAOT path
CoreCLR is the foundation for NativeAOT compilation, which produces fully ahead-of-time compiled native binaries with no JIT compiler or interpreter at runtime. With CoreCLR as the default on mobile, the path to NativeAOT on Android opens up as a natural next step.
On iOS and Mac Catalyst, Apple has always required ahead-of-time compilation — JIT execution is prohibited on those platforms. Previously, Mono handled this with its own AOT compiler. With CoreCLR, the AOT story moves to a more unified toolchain that shares infrastructure with NativeAOT on other platforms.
The practical benefits of NativeAOT on mobile are substantial:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net11.0-android;net11.0-ios;net11.0-maccatalyst</TargetFrameworks>
<!-- NativeAOT produces smaller, faster-starting binaries -->
<PublishAot>true</PublishAot>
</PropertyGroup>
</Project>
NativeAOT eliminates JIT warm-up entirely, produces smaller binaries (as low as 15 MB for a minimal app versus 30-40 MB with Mono), and enables full trimming and static analysis at build time. The trade-off is longer build times and restrictions on dynamic code patterns like Reflection.Emit and unconstrained MakeGenericType.
// TIP
NativeAOT on Android is still maturing. Use it for release builds where startup time matters, but stick with the default JIT mode during development for faster build-deploy cycles and Hot Reload support.
What about Blazor WebAssembly?
One important clarification: this change does not affect Blazor WebAssembly. WASM continues to use Mono, and Microsoft has stated this is not changing in .NET 11. The browser sandbox requires a runtime that can compile to WebAssembly, and CoreCLR doesn't target that environment.
The scope of the CoreCLR switch is Android, iOS, Mac Catalyst, and tvOS — the native mobile and desktop platforms where CoreCLR can run directly on the hardware.
Opting back to Mono
If you hit a blocking issue — a compatibility problem, an unacceptable performance regression, or a third-party library that behaves differently on CoreCLR — you can switch back to Mono with a single MSBuild property:
<PropertyGroup>
<!-- Temporarily revert to Mono while investigating CoreCLR issues -->
<UseMonoRuntime>true</UseMonoRuntime>
</PropertyGroup>
This works for Android, iOS, and Mac Catalyst. The opt-out will be available throughout .NET 11 servicing, giving teams time to identify and report issues.
// IMPORTANT
When you opt out, file an issue in dotnet/android or dotnet/macios with your app type, package size, startup timings, and a reproducible sample. The runtime team is using these reports to prioritise fixes before the November release.
What to watch for during migration
If you're moving an existing MAUI app from .NET 10 to .NET 11, here are the areas most likely to surface issues:
Reflection-heavy code — CoreCLR's reflection implementation differs from Mono's in subtle ways. Apps that use Type.GetType() extensively, build expression trees at runtime, or rely on MethodInfo.Invoke() hot paths should profile startup carefully.
Custom linker configurations — If your app uses custom linker descriptors or trimming annotations tuned for Mono's linker, review them against CoreCLR's trimming behaviour. The two linkers handle certain edge cases differently.
Third-party native bindings — Libraries that include platform-specific native code may need updates if they made assumptions about the hosting runtime. Most well-maintained libraries will work unchanged, but niche packages with Mono-specific interop should be tested.
Debug build performance — R2R is enabled by default in release builds, but debug builds use JIT compilation for faster iteration. If your debug experience feels different, that's expected — the trade-off favours development speed over runtime performance.
public partial class App : Application
{
public App()
{
InitializeComponent();
// If you're seeing startup regressions, instrument your app to measure
var sw = System.Diagnostics.Stopwatch.StartNew();
// ... initialisation code ...
sw.Stop();
System.Diagnostics.Debug.WriteLine($"App init: {sw.ElapsedMilliseconds}ms");
}
}
Common pitfalls
Assuming Mono and CoreCLR behave identically — They don't. Floating-point precision, exception ordering in certain edge cases, and GC finalisation timing can differ. Code that accidentally depended on Mono-specific behaviour may break.
Not benchmarking before upgrading — Establish a .NET 10 baseline for your app's startup time, memory usage, and key user flows before retargeting to .NET 11. Without a baseline, you can't distinguish CoreCLR regressions from other changes.
Using UseMonoRuntime as a permanent solution — The opt-out is a temporary escape hatch. Mono support on mobile will eventually be deprecated as CoreCLR matures. Invest in identifying and reporting issues rather than silently opting out.
Ignoring NativeAOT trimming warnings — If you plan to use NativeAOT, address trimming and AOT warnings now. CoreCLR's static analysis is stricter than Mono's, and warnings that were informational before may become errors.
Skipping iOS testing — iOS has always required AOT compilation, but the toolchain is different with CoreCLR. Test your app on physical iOS devices, not just simulators, to catch platform-specific issues early.
Summary
- CoreCLR is the default runtime for .NET MAUI on Android, iOS, and Mac Catalyst starting in .NET 11 Preview 4, ending the long-standing Mono/CoreCLR split
- Tiered JIT, ReadyToRun precompilation, PGO, and region-based GC all come to mobile for the first time
- Full
dotnet-trace,dotnet-counters, anddotnet-gcdumpsupport means server-class diagnostics on mobile devices - NativeAOT on Android becomes a natural next step, with startup improvements of up to 2.75x over Mono JIT
- Blazor WebAssembly is unaffected — WASM stays on Mono
- Set
<UseMonoRuntime>true</UseMonoRuntime>to opt out temporarily if you hit blocking issues - Benchmark your app against .NET 10 baselines before upgrading, particularly if it relies heavily on reflection or complex DI graphs