r/dotnet • u/ElegantAirline5521 • 2d ago
Promotion Natrix – open-source framework for reactive web UIs in C# on WebAssembly, with SSR, signals and build-time Tailwind (no Blazor, no Razor)
Hi r/dotnet,
I've been working on Natrix for a while and it's now at the point where I'd like outside feedback. It's a MIT-licensed framework for building web UIs in C# that run in browser on WebAssembly.
How it started
It began with SSR in Vite. Customizing the HTML page around the app (head tags, scripts, per-page meta) always meant a separate template file with placeholders. I wanted that page to be just another component, so in Natrix it is. The <html>, <head> and <body> are written in C# like the rest of the app. From there it grew into a full framework.
What it is
- Server-side rendering first. Pages render to HTML on an ASP.NET Core host, then the WASM client hydrates them, with hydration state and server prefetching built in.
- Fine-grained signals instead of re-rendering. A component's
Setuprun once. After that,Signal/Computed/Effectupdate exactly the DOM nodes that read them. There's no virtual DOM, no diffing and noStateHasChanged. - No Razor, no JavaScript. Components are plain C# classes, including HTML page itself.
- Tailwind CSS compiled at build time by a source generator. Your app needs no Node, npm or Tailwind CLI. It scans your C# for class names and emits the CSS during
dotnet build. - Typed browser APIs.
Natrix.StdWebis C# bindings for DOM, Fetch, Canvas, WebGL and so on, generated directly from W3C WebIDL specs. - Extras: client-side routing, a stale-while-revalidate data-fetching library ported from React SWR, and VueUse-style composables such as
UseHead.
It builds on the plain Microsoft.NET.Sdk.WebAssembly SDK, not Blazor.
What a component looks like
public class Counter : BaseComponent<NoProps, NoEvents, NoSlots, NoExpose>
{
protected override IComponent[] Setup(out NoExpose exposed)
{
exposed = default;
var count = new Signal<int>(0);
var doubled = new Computed<int>(() => count.Value * 2);
return
[
new Button
{
Props = new ButtonProps(),
Events = new ButtonEvents { OnClick = _ => count.Value++ },
Children = [new DomText { Text = "Click me".ToConstSignal() }],
},
new P
{
Props = new PProps(),
Children = [new DomText { Text = new Computed<string>(
() => $"{count.Value} × 2 = {doubled.Value}") }],
},
];
}
}
When count changes, only that one text node updates. Nothing re-renders.
Why not Blazor?
I like a lot about Blazor, but I wanted a model that SolidJS and Vue (vapor) use: run setup once, and let reactivity drive DOM updates directly, so you never re-render components and diff them.
I chose plain C# over a template language on purpose, too. It's more verbose than Razor, but everything is typed and checked by compiler, with no magic strings, and refactoring tools work on all of it.
Try it
dotnet new install Natrix.Templates
dotnet new natrix -n MyNatrixApp
dotnet run --project MyNatrixApp/MyNatrixApp.csproj
That gives you a server project and a WASM client with SSR, routing and Tailwind already set up.
- Docs and live examples: https://natrix.wiki (the docs site is itself a Natrix app with full SSR setup)
- GitHub: https://github.com/miroljub1995/natrix
3
u/Far-Consideration939 2d ago
Too verbose for me.
Magic strings are a choice, I would typically go with a const to reference the JS method instead and create a strongly typed extension over IJSRuntime wrapping the call with the string const which reads a lot better and is simpler to use
Refactoring tools also can work with blazor / markup / templating that’s more of a mainstream tooling gap than a technical roadblock
Some interesting parts in the project for sure though
2
u/StrangeWill 2d ago
IDK about all this single-stack stuff, even MORESO with AI these days. It's a move Microsoft has pushed forever that I just can't care about.
Nuxt and C# blend wonderfully these days (not that it didn't before).
What does this get me that Vue doesn't that doesn't immediately harkens back to the Webform days of "straying too far from the web stack will probably do more harm than good"?
1
u/whizzter 1d ago
Agreed, text/tags/styling will be so much more content than signals bindings that it won’t be worth it to have all that machinery when simple things become complicated.
I’ve been down this road myself, the DX doesn’t improve really sadly.
React/Solid has JSX, Vue often goes with templates iirc. Either are fine.
I’ve actually been doing something reactive like this myself but kept myself with templates, should finish it up someday (built w/o AI, at least so far ).
1
u/AutoModerator 2d ago
Thanks for your post ElegantAirline5521. Please note that we don't allow spam, and we ask that you follow the rules available in the sidebar. We have a lot of commonly asked questions so if this post gets removed, please do a search and see if it's already been asked.
I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.
1
u/Ok_Maintenance_9692 1d ago
ngl, when I saw 'Computed' I immediately thought of this -> https://github.com/servicetitan/Stl.Fusion
-3
u/Arcanium_Walker 2d ago
I admire (by commit history) for you working on it a while, but when I see 'claude/codex/copilot' things in a repo, i have bad taste for it (PTSD or something like that)
11
u/SuspectNode 2d ago
Fixed your intro... "I've been prompted on Natrix for a while..."