Native AOT
This document explains how to use Native Ahead-of-Time (AOT) compilation with RazorConsole to distribute native console applications without an installed .NET runtime and avoid JIT warm-up at startup.
Native AOT support in RazorConsole is currently experimental. While core features like routing and rendering are tested and working, you may encounter edge cases with third-party libraries or complex reflection scenarios. Please report any issues on GitHub.
Install the Native AOT Gallery
RazorConsole publishes the Component Gallery as native executables for Windows, Linux, and macOS on x64 and Arm64. These builds do not require the .NET SDK or runtime.
On macOS or Linux:
curl -fsSL https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.sh | sh -s -- --app GalleryOn Windows PowerShell:
& ([scriptblock]::Create((irm https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.ps1))) -App GalleryBoth installers resolve the latest GitHub Release, select the correct archive, and verify it against the published SHA-256 checksum before installing it. Manual archives and checksums-sha256.txt are available from the latest release.
To test the latest successful main build, select the nightly channel:
curl -fsSL https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.sh | sh -s -- --app Gallery --channel nightly& ([scriptblock]::Create((irm https://raw.githubusercontent.com/RazorConsole/RazorConsole/main/scripts/install-razor-console-app.ps1))) -App Gallery -Channel NightlyEvery nightly uses a unique nightly-<timestamp>-<commit> prerelease. CI creates it as a draft, uploads all six platform archives and checksums-sha256.txt, then publishes it. Formal releases use the same draft-first sequence. This workflow is compatible with GitHub immutable releases and prevents installers from selecting an incomplete build.
See the Component Gallery guide for installation directories, supported archives, and macOS Gatekeeper guidance.
1. What is Native AOT?
Native AOT compiles your .NET application directly into native machine code like other compiled languages does, rather than Intermediate Language (IL) that requires a JIT compiler at runtime.
Benefits for Console Apps:
- Startup: Native compilation removes JIT warm-up; actual startup time depends on the application.
- Standalone Distribution: No need to install the .NET runtime on the target machine.
- Native Executable: Include any runtime assets your app needs alongside the binary (for example, the Gallery's
Fonts/directory).
2. Prerequisites
To build Native AOT applications, you need platform-specific build tools installed on your development machine or CI environment. Look at this article in msdocs.
3. How to Publish
To publish your application as a native executable, use the standard dotnet publish command with the -p:PublishAot=true property.
You must specify a Runtime Identifier (RID), as native code is platform-specific.
# Publish for Linux
dotnet publish -c Release -r linux-x64 -p:PublishAot=true
# Publish for Windows
dotnet publish -c Release -r win-x64 -p:PublishAot=true
# Publish for macOS (Apple Silicon)
dotnet publish -c Release -r osx-arm64 -p:PublishAot=trueFor a standard SDK project, the resulting binary is located in bin/Release/{target-framework}/{rid}/publish/. Projects using the artifacts output layout, including this repository, use their configured artifacts directory instead.
To build the Gallery itself for the current macOS Apple Silicon host:
dotnet publish gallery/RazorConsole.Gallery/RazorConsole.Gallery.csproj \
--configuration Release \
--framework net10.0 \
--runtime osx-arm64 \
-p:PublishAot=true \
-p:StripSymbols=trueNative AOT supports cross-architecture compilation in some configurations, but not cross-OS compilation. Release builds therefore run on matching Windows, Linux, and macOS GitHub-hosted runners. The distributable archive must include both the executable and the Gallery Fonts/ directory.
4. Known Warnings & Limitations
4.1. The IL2104 Warning
During the build, you might see:
warning IL2104: Assembly 'Microsoft.AspNetCore.Components' produced trim warnings
Why this happens: Blazor was designed for browser scenarios where the full .NET runtime is available. Some internal Blazor APIs use reflection patterns that the AOT analyzer cannot verify.
Is it safe? Yes, for RazorConsole use cases. We've tested core features (routing, rendering, DI) and they work correctly. The warnings are about unused code paths in Blazor's browser-specific features.
Suppressing the warning:
<PropertyGroup>
<!-- Safe for RazorConsole console apps -->
<NoWarn>$(NoWarn);IL2104</NoWarn>
</PropertyGroup>When to investigate: If you're using advanced Blazor features beyond basic component rendering, test thoroughly with AOT.
4.2. Routing and Pages
By default, the .NET AOT compiler trims unused code aggressively. Because the Router finds pages via reflection, the trimmer might accidentally remove your page components if they aren't directly referenced.
To ensure routing works correctly, you must prevent your application assembly from being trimmed. Add this to your project file (.csproj):
<ItemGroup>
<TrimmerRootAssembly Include="$(AssemblyName)" />
</ItemGroup>4.3. Reflection & Parameters
Native AOT aggressively trims unused code. Anonymous types rely on reflection to read properties at runtime, and the trimmer may remove property metadata if it cannot statically prove the properties are used.
Avoid Anonymous Types for Parameters:
// Avoid this in AOT
// The trimmer may remove property metadata, causing runtime failures
var parameters = new { Title = "Hello", Count = 5 };Use Dictionary Instead:
Explicitly using Dictionary<string, object> ensures the AOT compiler preserves the data.
// Preferred way for AOT
var parameters = new Dictionary<string, object>
{
{ "Title", "Hello" },
{ "Count", 5 }
};
await renderer.RenderAsync<MyComponent>(parameters);4.4. What Works & What Doesn't
✅ AOT-Compatible:
- Razor component rendering
- Routing (
@pagedirectives) - Dependency injection
System.Text.Json(with source generators)- LINQ (query syntax)
⚠️ Requires Care:
- Third-party libraries (check for
IsAotCompatible) - Custom reflection code
- Dynamic assembly loading
❌ Not Supported:
System.Reflection.Emit- C# dynamic keyword
5. Troubleshooting
Build fails with error MSB3073 or link.exe not found (Windows)
This usually means the C++ Build Tools are missing.
- Open Visual Studio Installer.
- Modify your installation.
- Check Desktop development with C++.
App crashes immediately (Segmentation Fault / MissingMethodException)
If your app uses third-party libraries that rely heavily on reflection (e.g., JSON serializers other than System.Text.Json source generator), they might be incompatible with AOT.
- Try enabling the AOT analysis warnings in your project to see potential issues:
<PropertyGroup>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>6. Examples
You can see a working AOT setup in the RazorConsole.Gallery project.