MAUI Performance
Use this skill when the user reports a measurable performance symptom or asks
for a performance review. Measure first, then change the smallest thing that can
explain the symptom.
Workflow
Identify the symptom: startup, first page render, scrolling, navigation,
memory, image loading, or network-bound delays.
Inspect target frameworks and configuration. Prefer Release builds for
meaningful startup and runtime measurements.
For startup, use the MAUI CLI profiling surface:
maui profile startup --help
Then run the target app with the appropriate platform/device options.
For UI runtime issues, inspect visual tree depth and logs when DevFlow is
available.
Apply targeted fixes and re-measure.
High-Value Fix Areas
| Symptom |
Check |
| Slow startup |
Startup profile, excessive work in MauiProgram, synchronous I/O, eager service construction, font/image count |
| Slow bindings |
Missing x:DataType, reflection-heavy bindings, converters doing expensive work |
| Janky lists |
CollectionView inside ScrollView, complex templates, missing item sizing strategy, image decode size |
| Layout cost |
Deep nested layouts, unnecessary Grid nesting, repeated measure invalidations |
| Image memory |
Oversized source images, missing MAUI image resizing, unbounded remote image caching |
| Release regression |
Debug-only diagnostics leaking into Release, linker/trimming differences |
Image Memory Guidance
A 4000×4000 RGBA PNG decodes to ~64 MB in memory regardless of how small it
appears on screen. A 200×200 display-sized image decodes to ~160 KB — 400×
less memory. Apply these changes when large images cause scrolling or memory
issues:
- Resize images to display size before adding to the project.
- Use the
MauiImage build action in .csproj with BaseSize for automatic
platform-specific resizing:<MauiImage Include="Resources/Images/*.png" BaseSize="400,400" />
- Decode images to display size at runtime when loading from remote URLs.
BaseSize only applies to packaged build-time image resources; it does not
resize downloaded, camera, gallery, or user-imported images.
- Use WebP or SVG for icons and logos that need to scale.
- For list rows, generate or download thumbnails — never decode 4000 × 4000
images inside a
CollectionView item template.
- Do not wrap
CollectionView in ScrollView.
- Keep item templates shallow and use compiled bindings.
- Prefer fixed or predictable item sizing when possible.
- Load thumbnails sized for display, not full-resolution images.
- Avoid expensive work in property getters used by item templates.
Startup Guardrails
- Avoid network calls and file scans during app construction.
- Register services lazily when possible; do not instantiate heavy services just
to register them.
- Defer non-critical initialization until after the first page is visible.
- Keep debug-only logging and DevFlow setup behind
#if DEBUG.
Trimming and NativeAOT Guardrails
- Treat trim and NativeAOT issues as publish-build issues, not Debug-build
issues. Reproduce with the same publish properties used by the app.
- Do not silence trim warnings blindly. Warnings such as IL2026 indicate code
that may not be safe when members are removed.
- Prefer source-generated serializers and explicit registrations over reflection
discovery for app models and services used in trimmed builds.
- When reflection is required, keep the dependency explicit with attributes such
as
DynamicDependency or a linker descriptor, and document why it is needed.
- Test startup and the affected feature after publish; a successful Debug run is
not evidence that trimming or NativeAOT is safe.
For System.Text.Json in trimmed or NativeAOT builds, move startup JSON paths to
a source-generated context:
[JsonSerializable(typeof(List<Product>))]
internal partial class AppJsonContext : JsonSerializerContext
{
}
var products = JsonSerializer.Deserialize(
json,
AppJsonContext.Default.ListProduct);
Keep the context near app models or serialization infrastructure and add every
serialized DTO shape used by the startup path. Do not hide IL2026 with broad
warning suppressions when a source-generated serializer can make the dependency
explicit.
Validation Checklist
- There is a before/after measurement or a clear reason measurement is blocked.
- Startup investigations use
maui profile startup when applicable.
- Binding-heavy pages use
x:DataType.
- Lists avoid nested scrolling and oversized images.
- Trim/NativeAOT changes are validated with a publish build when applicable.
- Performance fixes do not remove accessibility metadata or automation hooks.
1---2name: maui-performance3description: Fix measurable MAUI performance issues. USE FOR: `maui profile startup`, Release/physical-device measurements, janky `CollectionView`, compiled bindings, oversized images, `MauiImage` `BaseSize`, thumbnail decoding, trim/NativeAOT regressions, IL2026, source-generated serializers, linker validation. DO NOT USE FOR: generic UI bugs, workload discovery, architecture-only work.4---56# MAUI Performance78Use this skill when the user reports a measurable performance symptom or asks9for a performance review. Measure first, then change the smallest thing that can10explain the symptom.1112## Workflow13141. Identify the symptom: startup, first page render, scrolling, navigation,15 memory, image loading, or network-bound delays.162. Inspect target frameworks and configuration. Prefer Release builds for17 meaningful startup and runtime measurements.183. For startup, use the MAUI CLI profiling surface:1920 ```bash21 maui profile startup --help22 ```2324 Then run the target app with the appropriate platform/device options.254. For UI runtime issues, inspect visual tree depth and logs when DevFlow is26 available.275. Apply targeted fixes and re-measure.2829## High-Value Fix Areas3031| Symptom | Check |32| --- | --- |33| Slow startup | Startup profile, excessive work in `MauiProgram`, synchronous I/O, eager service construction, font/image count |34| Slow bindings | Missing `x:DataType`, reflection-heavy bindings, converters doing expensive work |35| Janky lists | `CollectionView` inside `ScrollView`, complex templates, missing item sizing strategy, image decode size |36| Layout cost | Deep nested layouts, unnecessary `Grid` nesting, repeated measure invalidations |37| Image memory | Oversized source images, missing MAUI image resizing, unbounded remote image caching |38| Release regression | Debug-only diagnostics leaking into Release, linker/trimming differences |3940## Image Memory Guidance4142A 4000×4000 RGBA PNG decodes to ~64 MB in memory regardless of how small it43appears on screen. A 200×200 display-sized image decodes to ~160 KB — 400×44less memory. Apply these changes when large images cause scrolling or memory45issues:46471. Resize images to display size before adding to the project.482. Use the `MauiImage` build action in `.csproj` with `BaseSize` for automatic49 platform-specific resizing:50 ```xml51 <MauiImage Include="Resources/Images/*.png" BaseSize="400,400" />52 ```533. Decode images to display size at runtime when loading from remote URLs.54 `BaseSize` only applies to packaged build-time image resources; it does not55 resize downloaded, camera, gallery, or user-imported images.564. Use WebP or SVG for icons and logos that need to scale.575. For list rows, generate or download thumbnails — never decode 4000 × 400058 images inside a `CollectionView` item template.59606162- Do not wrap `CollectionView` in `ScrollView`.63- Keep item templates shallow and use compiled bindings.64- Prefer fixed or predictable item sizing when possible.65- Load thumbnails sized for display, not full-resolution images.66- Avoid expensive work in property getters used by item templates.6768## Startup Guardrails6970- Avoid network calls and file scans during app construction.71- Register services lazily when possible; do not instantiate heavy services just72 to register them.73- Defer non-critical initialization until after the first page is visible.74- Keep debug-only logging and DevFlow setup behind `#if DEBUG`.7576## Trimming and NativeAOT Guardrails7778- Treat trim and NativeAOT issues as publish-build issues, not Debug-build79 issues. Reproduce with the same publish properties used by the app.80- Do not silence trim warnings blindly. Warnings such as IL2026 indicate code81 that may not be safe when members are removed.82- Prefer source-generated serializers and explicit registrations over reflection83 discovery for app models and services used in trimmed builds.84- When reflection is required, keep the dependency explicit with attributes such85 as `DynamicDependency` or a linker descriptor, and document why it is needed.86- Test startup and the affected feature after publish; a successful Debug run is87 not evidence that trimming or NativeAOT is safe.8889For System.Text.Json in trimmed or NativeAOT builds, move startup JSON paths to90a source-generated context:9192```csharp93[JsonSerializable(typeof(List<Product>))]94internal partial class AppJsonContext : JsonSerializerContext95{96}9798var products = JsonSerializer.Deserialize(99 json,100 AppJsonContext.Default.ListProduct);101```102103Keep the context near app models or serialization infrastructure and add every104serialized DTO shape used by the startup path. Do not hide IL2026 with broad105warning suppressions when a source-generated serializer can make the dependency106explicit.107108## Validation Checklist109110- There is a before/after measurement or a clear reason measurement is blocked.111- Startup investigations use `maui profile startup` when applicable.112- Binding-heavy pages use `x:DataType`.113- Lists avoid nested scrolling and oversized images.114- Trim/NativeAOT changes are validated with a publish build when applicable.115- Performance fixes do not remove accessibility metadata or automation hooks.