One of the things I've been wanting to try for a while was whether Reactor could work nicely with .NET file-based apps.
Turns out: it can.
The idea is pretty simple. Instead of creating a full project, you put everything in a single App.cs file, add a few #: directives at the top, and run it directly with `dotnet run`.
The smallest example
Here's a complete Reactor app in one file:
#:property TargetFramework=net10.0-windows10.0.22621.0
#:property WindowsAppSDKSelfContained=true
#:package Microsoft.UI.Reactor@0.1.0-*
using Microsoft.UI.Reactor;
using Microsoft.UI.Reactor.Core;
using static Microsoft.UI.Reactor.Factories;
ReactorApp.Run<App>("SingleFileReactor", width: 900, height: 600);
class App : Component
{
public override Element Render()
{
var (name, setName) = UseState("World");
return VStack(
Heading($"Hello, {name}!"),
TextBox(name, setName, placeholderText: "Your name")
.AutomationName("NameInput")
).Padding(16);
}
}
Save that as `App.cs`, then run: dotnet run App.cs -a x64
The -a x64 part matters here. Since the script uses WindowsAppSDKSelfContained=true, you need to tell the build which Windows architecture you want (you could also add #:property RuntimeIdentifier=win-x64 to the header instead)
And that's really it. You get a native desktop window with a heading and a text box, and as you type your name, the greeting updates live.
What the directives do
The three lines at the top do most of the magic:
#:property TargetFramework=net10.0-windows10.0.22621.0
#:property WindowsAppSDKSelfContained=true
#:package Microsoft.UI.Reactor@0.1.0-*
The target framework makes this a Windows app targeting WinUI.
WindowsAppSDKSelfContained=true makes sure the Windows App SDK bits are available the way the app expects.
And the package line is just a normal NuGet dependency, except declared inline for the file-based app model.
A normal Reactor app created from the project template is still the right place to start for a "real" app. You get a .csproj, a proper project structure, and something you can keep growing.
But file-based apps open up a different kind of scenario.
Sometimes you don't want to start a project. Sometimes you just want a little tool.
Maybe you want:
- a quick internal utility
- a tiny prototype
- a one-off helper app
- a script that sometimes needs a proper interactive UI
That's where this gets really fun.
Mixing scripting with a real UI
I often use file-based apps for running little scripts with C# instead of using Batch or Bash. This got me the idea that you can create a script that can either run in plain console mode, or pop a UI if you ask for interactive mode.
Here's a cleaned up version of that:
#:property TargetFramework=net10.0-windows10.0.22621.0
#:property WindowsAppSDKSelfContained=true
#:package Microsoft.UI.Reactor@0.1.0-*
using Microsoft.UI.Reactor;
using Microsoft.UI.Reactor.Core;
using Microsoft.UI.Xaml;
using static Microsoft.UI.Reactor.Factories;
if (args.Length > 0 && (args[0] == "-i" || args[0] == "--interactive"))
{
Console.WriteLine("Waiting for user to enter their name...");
ReactorApp.Run<EnterUsernameApp>("Username", width: 300, height: 130);
}
else
{
Console.WriteLine("Enter your user name:");
AppState.Username = Console.ReadLine() ?? string.Empty;
}
if (string.IsNullOrEmpty(AppState.Username))
{
Console.WriteLine("No username entered.");
return -1;
}
Console.WriteLine($"Hello {AppState.Username}!");
return 0;
class EnterUsernameApp : Component
{
public override Element Render()
{
var (name, setName) = UseState("");
return VStack(
TextBox(name, setName, placeholderText: "Enter username")
.AutomationName("UsernameInput"),
Button("Accept", () =>
{
AppState.Username = name;
ReactorApp.PrimaryWindow?.Close();
})
.IsEnabled(!string.IsNullOrEmpty(name))
.HAlign(HorizontalAlignment.Stretch)
).Padding(10);
}
}
static class AppState
{
public static string Username { get; set; } = string.Empty;
}
Run it in console mode: dotnet run App.cs -a x64
Or launch the UI mode: dotnet run App.cs -a x64 -- -i
If you're writing automation or a developer tool, that can be incredibly useful. Most of the time maybe the script can stay headless and work in the terminal. But if the user needs to make a choice, enter a value, or confirm something in a friendlier way, you can just pop a real window. Another example could be that you can either provide filenames as arguments, or if you don't provide this as argument, a UI allowing you to browse to the required files instead.
Is this how you should build every app?
Nope! Once the app grows beyond a quick tool or prototype, a normal project structure is still going to be much easier to maintain.
But for small utilities? Demos? Quick experiments? Interactive scripts, this is really nice.
I especially like that it lowers the bar for building little native Windows helpers. If I can keep a whole app in one file and still get a proper WinUI window, I'm far more likely to reach for a native UI instead of settling for a clunky prompt loop.
And that's probably the biggest compliment I can give this setup: it makes a real Windows UI feel cheap enough to use for the small stuff too.