$npx -y skills add wshaddix/dotnet-skills --skill dotnet-aot-wasmAOT-compiling for WebAssembly. Blazor/Uno WASM AOT, size vs speed, lazy loading, Brotli.
| 1 | # dotnet-aot-wasm |
| 2 | |
| 3 | WebAssembly AOT compilation for Blazor WASM and Uno WASM applications: compilation pipeline, download size vs runtime speed tradeoffs, trimming interplay, lazy loading assemblies, and Brotli pre-compression for download optimization. |
| 4 | |
| 5 | **Version assumptions:** .NET 8.0+ baseline. Blazor WASM AOT shipped in .NET 6 and has been refined through .NET 8-10. Uno WASM uses a similar compilation pipeline with Uno-specific tooling. |
| 6 | |
| 7 | **Important tradeoff:** Trimming and AOT have **opposite effects** on WASM artifact size. Trimming reduces download size by removing unused code. AOT **increases** artifact size (native WASM code is larger than IL) but **improves** runtime execution speed. Use both together for the best balance. |
| 8 | |
| 9 | **Out of scope:** Native AOT for server-side .NET -- see [skill:dotnet-native-aot]. AOT-first design patterns -- see [skill:dotnet-aot-architecture]. Trim-safe library authoring -- see [skill:dotnet-trimming]. MAUI-specific AOT -- see [skill:dotnet-maui-aot]. Blazor component patterns and architecture -- see [skill:dotnet-blazor-patterns] (soft). Uno Platform architecture -- see [skill:dotnet-uno-platform] (soft). |
| 10 | |
| 11 | Cross-references: [skill:dotnet-native-aot] for general AOT pipeline, [skill:dotnet-trimming] for trimming annotations, [skill:dotnet-aot-architecture] for AOT-safe design patterns, [skill:dotnet-serialization] for AOT-safe serialization, [skill:dotnet-csharp-source-generators] for source gen as AOT enabler, [skill:dotnet-blazor-patterns] for Blazor architecture (soft), [skill:dotnet-uno-platform] for Uno Platform patterns (soft). |
| 12 | |
| 13 | --- |
| 14 | |
| 15 | ## Download Size vs Runtime Speed |
| 16 | |
| 17 | Understanding the size/speed tradeoff is critical for WASM AOT decisions: |
| 18 | |
| 19 | | Compilation Mode | Download Size | Runtime Speed | Startup Time | |
| 20 | |-----------------|---------------|---------------|-------------| |
| 21 | | IL interpreter (no AOT) | Smallest | Slowest | Fastest startup | |
| 22 | | AOT (all assemblies) | **Largest** | Fastest | Slower startup | |
| 23 | | AOT (selective) + trimming | Balanced | Good | Moderate | |
| 24 | | Trimmed only (no AOT) | Small | Moderate (JIT interpretation) | Fast | |
| 25 | |
| 26 | **Key insight:** Trimming reduces size by removing unused IL. AOT **increases** total artifact size because compiled native WASM code is larger than the equivalent IL bytecode. However, AOT-compiled code executes significantly faster because it skips IL interpretation at runtime. |
| 27 | |
| 28 | ### When to Use WASM AOT |
| 29 | |
| 30 | - **CPU-intensive workloads:** Image processing, complex calculations, data transformation |
| 31 | - **Predictable performance:** Consistent execution speed without JIT pauses |
| 32 | - **Hot paths:** AOT-compile only performance-critical assemblies (selective AOT) |
| 33 | |
| 34 | ### When to Skip WASM AOT |
| 35 | |
| 36 | - **Bandwidth-constrained users:** AOT increases download size significantly |
| 37 | - **Simple CRUD apps:** IL interpretation is fast enough for UI interactions and API calls |
| 38 | - **Rapid iteration:** AOT compilation adds significant publish time |
| 39 | |
| 40 | --- |
| 41 | |
| 42 | ## Blazor WASM AOT |
| 43 | |
| 44 | ### Enabling AOT |
| 45 | |
| 46 | ```xml |
| 47 | <!-- Blazor WASM .csproj --> |
| 48 | <PropertyGroup> |
| 49 | <RunAOTCompilation>true</RunAOTCompilation> |
| 50 | </PropertyGroup> |
| 51 | ``` |
| 52 | |
| 53 | ```bash |
| 54 | # Publish with AOT (required -- AOT only applies during publish) |
| 55 | dotnet publish -c Release |
| 56 | ``` |
| 57 | |
| 58 | Note: `RunAOTCompilation` is the Blazor WASM property (not `PublishAot` which is for server-side Native AOT). AOT compilation only happens during `dotnet publish`, not during `dotnet run` or `dotnet build`. |
| 59 | |
| 60 | ### Selective AOT via Lazy Loading |
| 61 | |
| 62 | Blazor WASM AOT compiles all non-lazy-loaded assemblies. To control which assemblies are AOT-compiled, mark non-critical assemblies as lazy-loaded -- they will use IL interpretation instead: |
| 63 | |
| 64 | ```xml |
| 65 | <PropertyGroup> |
| 66 | <RunAOTCompilation>true</RunAOTCompilation> |
| 67 | </PropertyGroup> |
| 68 | |
| 69 | <ItemGroup> |
| 70 | <!-- These assemblies are NOT AOT-compiled (loaded on demand via IL interpreter) --> |
| 71 | <BlazorWebAssemblyLazyLoad Include="MyApp.Reporting.wasm" /> |
| 72 | <BlazorWebAssemblyLazyLoad Include="MyApp.Admin.wasm" /> |
| 73 | <!-- All other assemblies (MyApp.Core, MyApp.Calculations, etc.) ARE AOT-compiled --> |
| 74 | </ItemGroup> |
| 75 | ``` |
| 76 | |
| 77 | ### Trimming + AOT Together |
| 78 | |
| 79 | For the best balance, use both trimming and AOT: |
| 80 | |
| 81 | ```xml |
| 82 | <PropertyGroup> |
| 83 | <!-- Trimming reduces unused code (smaller download) --> |
| 84 | <PublishTrimmed>true</PublishTrimmed> |
| 85 | |
| 86 | <!-- AOT compiles remaining code to native WASM (faster execution) --> |
| 87 | <RunAOTCompilation>true</RunAOTCompilation> |
| 88 | |
| 89 | <!-- Detailed warnings during development --> |
| 90 | <EnableTrimAnalyzer>true</EnableTrimAnalyzer> |
| 91 | </PropertyGroup> |
| 92 | ``` |
| 93 | |
| 94 | The publish pipeline runs: trim unused IL first, then AOT-compile the remaining assemblies to native WASM. This produces an artifact that is larger than trimmed-only but smaller than AOT-without-trimming, with the best runtime performance. |
| 95 | |
| 96 | --- |
| 97 | |
| 98 | ## Uno WASM AOT |
| 99 | |
| 100 | Uno Platform 5+ with .NET 8+ uses the standard .NET WASM workload, so the AOT configuration is the same as |