Skip to main content

Quick start

This is the Android path, because the Android path is the template. iOS is a manual procedure against a reference shell — see Shells → iOS.

If dotnet new blazornative is not found, read Installation first — the template is published on nuget.org, and dotnet new install BlazorNative.Templates is the one-line install.

1. Create the app

dotnet new blazornative -n MyApp
cd MyApp

You get a .NET app (your pages, the page manifest, the NativeAOT publish head) and a runnable Android/Gradle tree with the shell already in it.

2. Publish, then gradle — always in that order

Your .NET app compiles to a native library; the Android shell loads it and renders its frames. The APK is built from the output of the publish, so the publish comes first:

# 1. the native library, for the ABI you are targeting
dotnet publish . -c Release -r linux-bionic-x64 # the emulator's ABI
dotnet publish . -c Release -r linux-bionic-arm64 # real devices

# 2. the APK
cd android
./gradlew installDebug # onto a running emulator/device

gradlew fails fast and names the exact publish command if a .so is missing. It will not hand you an APK with stale native assets — the failure you would otherwise get is an app that launches and shows nothing.

3. The win-x64 publish

dotnet publish . -c Release -r win-x64

That produces the loadable native BlazorNative.Runtime.dll — the same C-ABI, the same patches, the same widget tree as on device. It exposes the exact export surface a JNA host would bind to. Note the template ships no runnable desktop host today: this publish gives you the loadable .dll, not an app you can launch without writing a host of your own.

Adding a page

A page is declared once — one row in AppPages.All. The runtime's mount registry and route table are derived views of that array, so they cannot drift from it:

BlazorNativePage.Routed<BnAboutPage>("/about", "BnAboutPage"), // route + mount name
BlazorNativePage.Named<SomeScreen>("SomeScreen"), // mount name only
Everything derives — including the deep-link map

Adding a routed page is that one row and nothing else. Android's deep-link map (res/raw/blazornative_routes.json, read at Intent-parse time before the native library loads) used to be a hand-written mirror in MainActivity.kt you had to keep in sync — the last place a page lived twice. Since v0.3.0 it is generated from AppPages.All at build time (BlazorNative.RouteGen reads your routed rows and emits the resource), so it cannot drift from your pages and there is no shell file to edit. Add the row; the deep link works.

Writing pages — the subset that actually renders

Only Bn* components render. There is no DOM here: <div>, <span> and <p> are not widgets and will not appear. The BlazorNative.Analyzers package is referenced for you and catches the common mistakes at compile time — see Analyzer rules.

Two rules worth knowing before they bite:

  • Components referenced in markup must be public.
  • Keep @using Microsoft.AspNetCore.Components.Web in _Imports.razor. Without it, @bind and @onclick compile to literal markup — no diagnostics, a green build, and a page that is silently dead on a native shell.

Two things not to delete

  • <TrimmerRootAssembly Include="$(AssemblyName)" /> in the csproj. Without it, ILC trims your entire app: green build, zero trim warnings, and a failed mount at runtime. The line carries a comment explaining the whole failure signature.
  • build/BionicNativeAot.targets and global.json. The first is the NDK cross-compile shim — there is no bionic publish without it. The second pins the SDK feature band the ILC host relies on.

Next

  • Components — what you can put on a page.
  • Layout and Yoga — how things get placed, and the one BnScroll rule that catches everybody.