This document explains how the Avalonia dotnet new templates are structured, how to build and test them locally, and how to add new templates or parameters. If you only want to use the templates, see the README.md instead.
This guide covers the Avalonia-specific conventions. For a step-by-step walkthrough, see the dotnet template tutorial; for the full template.json schema and options, see the .NET custom templates reference and the dotnet/templating samples.
Everything ships as a single NuGet package, Avalonia.Templates, defined by Avalonia.Templates.csproj. The .csproj doesn't compile any code, it only packs everything under templates/ into the package.
Each template lives in its own folder under templates/ and is described by a .template.config/ directory:
| File | Purpose |
|---|---|
template.json |
The template definition, including parameters (symbols). |
dotnetcli.host.json |
Maps parameters to their CLI arguments. |
ide.host.json |
Maps parameters to their IDE settings. |
C# and F# variants are kept as separate templates under templates/csharp/ and templates/fsharp/, but share the same short name (e.g. avalonia.app) and are told apart by their language tag.
To try your changes, pack the templates and install them from the local package:
./install-dev-templates.ps1This uninstalls any existing Avalonia.Templates, runs dotnet pack, and installs the freshly built .nupkg.
Templates are tested by generating projects from them and building the results.
The test script is tests/build-test.ps1. Run it from the tests folder:
cd tests
./build-test.ps1It creates every application template with different combinations of parameters and builds each one, in parallel, to make sure it compiles.
Each build writes its own binary log to binlog/; pass -ThrottleLimit <n> to change how many builds run at once (it defaults to the CPU count).
Pass -NuGetSource <url> to restore from an extra feed, e.g. to test the templates against an Avalonia nightly build.
The build matrix is data-driven: every variant is a New-Case row in the $builds array, and the item templates instantiated into the MVVM projects are New-ItemCase rows in $itemTemplates.
When you add a new template or parameter, add a matching New-Case row so it gets built in CI.
Add a new template under templates/csharp/<your-template>/ (and templates/fsharp/... for an F# variant), with a unique identity and shortName in template.json, and update the CLI/IDE files if it takes parameters (see below).
The existing templates are the best reference.
The C# and F# variants share a shortName and groupidentity, but each identity must be unique — a duplicate makes the engine register only one, so dotnet new <name> -lang C# fails with "Allowed values for '-lang' option are: 'F#'". Convention: suffix the F# identity with .FSharp.
Finally, add build coverage with a New-Case row in tests/build-test.ps1 and document it in README.md.
A parameter is a symbol in template.json, given a CLI name in dotnetcli.host.json and an IDE label in ide.host.json.
Make sure to update all three files. Without the CLI and IDE entries, the parameter won't be usable from the command line or shown in IDEs.
Mirror the parameter across the C# and F# variants — don't expose an option a language doesn't actually implement, or it becomes a silent no-op (F# xplat once declared MainViewPageType while its MainView ignored it). Removing a parameter also means deleting its computed symbols and sources[].modifiers conditions, not just the three host files; grep the template folder for the symbol name first.
Add test coverage with a New-Case row in tests/build-test.ps1 for each value of a choice/bool parameter, not just the default — #if branches only compile when the value is selected, so a broken non-default branch passes CI until something builds it. Document the parameter in README.md.