If you've ever wondered why your .NET MAUI app feels painfully sluggish during debugging but runs perfectly fine in release mode — yeah, you're not alone. The culprit has always been runtime XAML inflation, a reflection-heavy process that parses your XAML at runtime. It works, but it's slow. Really slow.
With .NET 10, Microsoft introduced XAML Source Generation (SourceGen), a Roslyn-powered approach that transforms your XAML into strongly-typed C# at compile time. The result? Up to 100x faster page inflation in debug mode and roughly 25% faster in release builds. That's not a typo.
This guide covers everything you need to know about XAML Source Generation — enabling it, configuring per-file overrides, inspecting generated code, and troubleshooting the gotchas. Whether you're starting fresh or upgrading an existing project, this is honestly the single most impactful performance win you can get in .NET MAUI right now.
Understanding XAML Inflation Modes in .NET MAUI
Before we get into SourceGen itself, it helps to understand how .NET MAUI has traditionally handled XAML. There are three inflation modes, and each comes with its own trade-offs.
Runtime Inflation (Reflection-Based)
This is the default for debug builds in .NET 10. The XAML parser reads your .xaml files at runtime, resolves types via reflection, and constructs the visual tree dynamically. It supports XAML Hot Reload and sends diagnostics to the IDE, but it's the slowest option — and it only catches syntax errors at runtime. Not ideal.
XamlC (IL Compilation)
The default for release builds. XamlC converts your markup into Intermediate Language (IL) during the build process. You get compile-time error checking, smaller assemblies (since .xaml files aren't embedded anymore), and faster page loading than runtime inflation. The downside? It doesn't produce debuggable output, and it can behave differently from debug builds, which sometimes masks bugs.
SourceGen (C# Source Generation)
New in .NET 10, and this is the exciting one. SourceGen uses Roslyn source generators to transform XAML into plain C# code at compile time. It gives you compile-time validation, consistent behavior across debug and release, massive performance improvements, and — here's the part I really like — the ability to set breakpoints directly in the generated code.
| Feature | Runtime | XamlC | SourceGen |
|---|---|---|---|
| Debug default (.NET 10) | Yes | No | No (opt-in) |
| Release default (.NET 10) | No | Yes | No (opt-in) |
| Compile-time error detection | No | Yes | Yes |
| Debug/Release consistency | No | No | Yes |
| Debuggable generated code | N/A | No | Yes |
| Debug inflation speed | Slowest | N/A | ~100x faster |
| Release inflation speed | Slowest | Fast | ~25% faster |
| Memory allocations | Highest | Low | Lowest |
Enabling XAML Source Generation Project-Wide
The simplest way to turn on SourceGen is a single MSBuild property in your .csproj file. This activates source generation for every XAML file across both debug and release:
<PropertyGroup>
<TargetFrameworks>net10.0-android;net10.0-ios;net10.0-maccatalyst</TargetFrameworks>
<MauiXamlInflator>SourceGen</MauiXamlInflator>
</PropertyGroup>
After adding this, clean and rebuild your solution. Every XAML page and view in your project will now be processed by the Roslyn source generator instead of the runtime inflator or XamlC.
If you manage multiple .NET MAUI projects in a single solution, you can set this once in a Directory.Build.props file at the solution root:
<!-- Directory.Build.props -->
<Project>
<PropertyGroup>
<MauiXamlInflator>SourceGen</MauiXamlInflator>
</PropertyGroup>
</Project>
One line, and every project in the solution picks it up. Nice and clean.
Configuring SourceGen Per File or Folder
Sometimes you'll want SourceGen for most of your project but need to exclude specific files that trigger known issues. The MauiXaml item group gives you that fine-grained control:
<ItemGroup>
<!-- Enable SourceGen for all XAML files -->
<MauiXaml Update="**/*.xaml" Inflator="SourceGen" />
<!-- Revert a specific folder to defaults (Runtime for Debug, XamlC for Release) -->
<MauiXaml Update="Controls\**.xaml" Inflator="Default" />
<!-- Force runtime inflation for a problematic file -->
<MauiXaml Update="Views\LegacyPage.xaml" Inflator="Runtime" />
</ItemGroup>
This is great for gradual migration. You can flip on SourceGen for new pages right away while keeping existing pages on the default inflator until you've sorted out any compatibility issues.
Suppressing Deprecation Warnings on Specific Files
If a XAML file references deprecated APIs, the generated C# code might emit compiler warnings. You can suppress these per file:
<ItemGroup>
<MauiXaml Update="Views\OldSettingsPage.xaml" Inflator="SourceGen" NoWarn="0612;0618" />
</ItemGroup>
Inspecting the Generated C# Code
One of the genuinely cool things about SourceGen is that you can actually read and debug the generated code. There are two ways to get at it.
Method 1: Emit Generated Files to Disk
Add this property to your .csproj:
<PropertyGroup>
<MauiXamlInflator>SourceGen</MauiXamlInflator>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
After building, the generated files show up under:
obj/{Configuration}/{TFM}/generated/
Microsoft.Maui.Controls.SourceGen/
Microsoft.Maui.Controls.SourceGen.CodeBehindGenerator/
*.xsg.cs
Each .xsg.cs file corresponds to a XAML file and contains the strongly-typed C# code that constructs your visual tree.
Windows path length tip: If you hit build errors on Windows because the generated file paths are absurdly long, set a short output path:
<PropertyGroup>
<CompilerGeneratedFilesOutputPath>C:\gen</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
Method 2: Use the Visual Studio Analyzers Node
In Visual Studio, go to Solution Explorer → Your Project → Dependencies → [TFM] (e.g., net10.0-android) → Analyzers → Microsoft.Maui.Controls.SourceGen → Microsoft.Maui.Controls.SourceGen.CodeBehindGenerator. The .xsg files listed there are your generated source. You can open them, set breakpoints, and step through the code during debugging — which is fantastic for understanding exactly what MAUI does with your markup.
What the Generated Code Looks Like
So what does SourceGen actually produce? Let's look at a simple XAML page:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Views.HomePage"
Title="Home">
<VerticalStackLayout Padding="20" Spacing="10">
<Label Text="Welcome to .NET MAUI"
FontSize="24"
HorizontalOptions="Center" />
<Button Text="Get Started"
Clicked="OnGetStartedClicked" />
</VerticalStackLayout>
</ContentPage>
With SourceGen enabled, the generator produces a .xsg.cs file containing C# code that directly constructs the VerticalStackLayout, Label, and Button. No XML parsing, no reflection, no runtime type resolution. Each property assignment is a direct, strongly-typed C# setter, and the generated types are decorated with the [Generated] attribute for tooling integration.
Compare that to runtime inflation, where each element is created through Activator.CreateInstance and properties are set via reflection-based type converters. It's night and day.
SourceGen and Compiled Bindings: Better Together
XAML Source Generation and compiled bindings are complementary features, and they deliver maximum performance when you use them together. Compiled bindings resolve binding expressions at compile time using the x:DataType attribute:
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:MyApp.ViewModels"
x:Class="MyApp.Views.ProductPage"
x:DataType="vm:ProductViewModel">
<VerticalStackLayout>
<Label Text="{Binding Name}" FontSize="20" />
<Label Text="{Binding Price, StringFormat='${0:F2}'}" />
<Label Text="{Binding Description}" />
<Button Text="Add to Cart" Command="{Binding AddToCartCommand}" />
</VerticalStackLayout>
</ContentPage>
With both SourceGen and compiled bindings active, the generated code produces strongly-typed property accessors instead of reflection-based binding resolution. Microsoft reports that compiled bindings are up to 20x faster than classic bindings, and I've seen similar numbers in my own testing.
Here's the important part: compiled bindings and the XAML source generator work hand-in-hand with trimming and NativeAOT. Because they generate strongly-typed code at build time, the trimmer can see exactly which properties are used and preserve them. So enabling compiled bindings everywhere isn't just a performance optimization — it's a prerequisite for NativeAOT deployment.
Performance Impact: Real-World Numbers
The performance gains from XAML Source Generation are substantial and well-documented by the .NET MAUI team:
- Debug inflation speed: Up to 100x (10,000%) faster than runtime reflection-based inflation
- Release inflation speed: Approximately 25% faster than XamlC-compiled inflation
- Memory allocations: Reduced proportionally to the speed improvements
- Combined with compiled bindings: Binding resolution up to 20x faster than classic string-based bindings
These improvements compound with every page and control in your app. An application with 30 pages will see a much more dramatic startup improvement than one with 5, because each page's visual tree construction is individually accelerated.
When You'll Feel It Most
In my experience, the biggest difference shows up in these scenarios:
- Debug builds on physical devices: That frustrating lag between tapping "Run" and actually seeing your app? It's basically gone.
- Apps with complex XAML pages: Pages with deep visual trees and lots of controls benefit the most
- CollectionView with DataTemplates: Each item template inflates faster, so scroll performance during debugging improves noticeably
- Shell-based navigation: Page transitions feel snappy instead of sluggish in debug mode
Troubleshooting Common SourceGen Issues
XAML Source Generation is pretty stable in .NET 10, but there are some known gotchas worth knowing about.
x:Reference Not Supported
The x:Reference markup extension isn't fully supported by the XAML source generator yet. If your XAML uses Source={x:Reference someControl}, you'll need to fall back to the default inflator for that file:
<ItemGroup>
<MauiXaml Update="Views\PageWithReferences.xaml" Inflator="Default" />
</ItemGroup>
FlexLayout.Basis Type Conversion Error
There's a known regression in .NET 10 that causes a build error — "cannot convert from 'double' to 'float'" — when using FlexLayout.Basis with SourceGen. Until Microsoft patches this, just fall back the affected file to the default inflator. Annoying, but manageable.
Invalid Bindings May Not Emit Errors
This one's a bit sneaky. There's a known issue where SourceGen doesn't report errors for invalid x:DataType declarations or incorrect binding paths. XamlC catches these correctly. So if you rely heavily on compile-time binding validation, test your pages in release mode (which uses XamlC by default) in addition to SourceGen debug builds.
Build Errors with EmitCompilerGeneratedFiles on Windows
When EmitCompilerGeneratedFiles is enabled, generated file paths can exceed Windows' 260-character limit. The fix is straightforward:
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>C:\gen</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
CommunityToolkit.Mvvm Compatibility with AOT
If you're using MauiXamlInflator=SourceGen together with CommunityToolkit.Mvvm source generators in iOS release AOT mode, you might run into conflicts. Make sure you're on the latest CommunityToolkit packages (9.x or later) and that all source generators are compatible with your target framework.
General Troubleshooting Steps
- Clean and rebuild: Delete
binandobjfolders to remove stale generated files - Update all NuGet packages: Ensure all
Microsoft.Maui.*packages are at the same version - Check the MAUI1001 warning: This build warning shows up when you manually set
RuntimeorXamlCand tells you about the trade-offs - Isolate the issue: Use per-file inflator overrides to narrow down which XAML file is causing the problem
- Report issues: If SourceGen fails on valid XAML, report it at the dotnet/maui GitHub repository — the team actively tracks SourceGen regressions
Migration Strategy: Adopting SourceGen in an Existing Project
Got an existing .NET MAUI project? Here's how to adopt XAML Source Generation without breaking everything.
Step 1: Update to .NET 10
XAML Source Generation requires .NET MAUI 10 or later. Update your target frameworks and all NuGet packages accordingly. (If you're still on .NET 8 or 9, this is a great excuse to finally make that jump.)
Step 2: Enable Compiled Bindings First
Before flipping on SourceGen, make sure all your XAML pages use x:DataType for compiled bindings. This gives you compile-time binding validation and prepares your codebase for the strongly-typed code that SourceGen generates. Think of it as laying the groundwork.
Step 3: Enable SourceGen Per File
Start with a few simple pages to validate compatibility:
<ItemGroup>
<MauiXaml Update="Views\HomePage.xaml" Inflator="SourceGen" />
<MauiXaml Update="Views\AboutPage.xaml" Inflator="SourceGen" />
</ItemGroup>
Build and test these on all target platforms before moving on.
Step 4: Expand to All Files
Once you're confident things are working, switch to project-wide SourceGen:
<PropertyGroup>
<MauiXamlInflator>SourceGen</MauiXamlInflator>
</PropertyGroup>
If specific files cause trouble, exclude them with per-file overrides as shown earlier.
Step 5: Test on All Platforms
Run your full test suite on Android, iOS, and any other targets. Pay close attention to pages with complex data templates, custom controls, and platform-specific renderers. These are the areas where you're most likely to hit edge cases.
Looking Ahead: SourceGen in .NET 11
The direction here is pretty clear. In .NET 11 (Preview 1 dropped in February 2026), SourceGen becomes the default inflator for all .NET MAUI projects. Here's what's coming:
- Default in all project templates: New projects will have
MauiXamlInflator=SourceGenset automatically - CoreCLR as default Android runtime: Combined with SourceGen, this means even faster startup on Android
- Inline C# expressions in XAML: .NET 11 introduces the ability to embed C# expressions directly in XAML property values, powered by the source generator — this is going to be a game changer for complex value conversions
- Trimming-safe EventTriggers: New generic
EventTrigger<T>types replace reflection-based triggers for 100% AOT compatibility - Hot Reload improvements: Better Hot Reload support for projects using SourceGen with project references
Adopting SourceGen now positions your project for a smooth upgrade to .NET 11 and prepares your codebase for the NativeAOT and trimming features that are becoming increasingly important for mobile performance. Don't wait — the migration is straightforward, and the benefits are immediate.
Frequently Asked Questions
Does XAML Source Generation work with XAML Hot Reload?
It depends. Runtime inflation still has the most complete Hot Reload support. SourceGen offers partial Hot Reload support that's actively being improved. For the best experience during active UI development, you can temporarily switch individual files back to the Runtime inflator and re-enable SourceGen before committing. A bit of a workaround, but it works well in practice.
Is XAML Source Generation compatible with NativeAOT and trimming?
Yes, and this is one of its key advantages. SourceGen produces strongly-typed C# code that the trimmer can analyze statically. Combined with compiled bindings (x:DataType), it eliminates the reflection-based code paths that cause problems with trimming and NativeAOT. If you're planning to ship a trimmed or AOT-compiled .NET MAUI app, enabling SourceGen with compiled bindings is basically required.
Can I use SourceGen with third-party control libraries like Syncfusion or Telerik?
In most cases, yes. Third-party controls that follow standard .NET MAUI patterns work fine with SourceGen. However, controls that rely on dynamic XAML features like x:Reference or runtime type resolution might need a per-file fallback to the default inflator. Always test after enabling SourceGen and check the vendor's docs for any compatibility notes.
What's the difference between XAML Source Generation and compiled bindings?
They're complementary. XAML Source Generation controls how your XAML markup gets transformed into a visual tree (replacing runtime reflection with compile-time C# code). Compiled bindings control how data binding expressions are resolved (replacing string-based reflection lookups with strongly-typed property accessors). Use both together for maximum performance.
Should I enable SourceGen for all files or migrate incrementally?
For new projects, enable it project-wide from day one — there's no reason not to. For existing projects, go incremental. Start with simple pages, verify they work on all platforms, and expand gradually. Use per-file overrides to exclude any pages that cause issues until you can resolve them. Most well-structured XAML pages work with SourceGen without any modifications.